home *** CD-ROM | disk | FTP | other *** search
/ Amiga Plus 2004 #11 / Amiga Plus CD - 2004 - No. 11.iso / AmiSoft / Comm / www / tidy_os4.lha / tidy / src / access.c next >
C/C++ Source or Header  |  2004-07-25  |  106KB  |  3,673 lines

  1. /* access.c -- carry out accessibility checks
  2.  
  3.   Copyright University of Toronto
  4.   Portions (c) 1998-2004 (W3C) MIT, ERCIM, Keio University
  5.   See tidy.h for the copyright notice.
  6.   
  7.   CVS Info :
  8.  
  9.     $Author: hoehrmann $ 
  10.     $Date: 2004/06/18 20:58:38 $ 
  11.     $Revision: 1.11 $ 
  12.  
  13. */
  14.  
  15. /*********************************************************************
  16. * AccessibilityChecks
  17. *
  18. * Carries out processes for all accessibility checks.  Traverses
  19. * through all the content within the tree and evaluates the tags for
  20. * accessibility.
  21. *
  22. * To perform the following checks, 'AccessibilityChecks' must be
  23. * called AFTER the tree structure has been formed.
  24. *
  25. * If, in the command prompt, there is no specification of which
  26. * accessibility priorities to check, no accessibility checks will be 
  27. * performed.  (ie. '1' for priority 1, '2' for priorities 1 and 2, 
  28. *                  and '3') for priorities 1, 2 and 3.)
  29. *
  30. * Copyright University of Toronto
  31. * Programmed by: Mike Lam and Chris Ridpath
  32. * Modifications by : Terry Teague (TRT)
  33. *
  34. *********************************************************************/
  35.  
  36. #include "tidy-int.h"
  37.  
  38. #if SUPPORT_ACCESSIBILITY_CHECKS
  39.  
  40. #include "access.h"
  41. #include "message.h"
  42. #include "tags.h"
  43. #include "attrs.h"
  44. #include "tmbstr.h"
  45.  
  46.  
  47. /* 
  48.     The accessibility checks to perform depending on user's desire.
  49.  
  50.     1. priority 1
  51.     2. priority 1 & 2
  52.     3. priority 1, 2, & 3
  53. */
  54.  
  55. /* List of possible image types */
  56. static const ctmbstr imageExtensions[] =
  57. {".jpg", ".gif", ".tif", ".pct", ".pic", ".iff", ".dib",
  58.  ".tga", ".pcx", ".png", ".jpeg", ".tiff", ".bmp"};
  59.  
  60. #define N_IMAGE_EXTS (sizeof(imageExtensions)/sizeof(ctmbstr))
  61.  
  62. /* List of possible sound file types */
  63. static const ctmbstr soundExtensions[] =
  64. {".wav", ".au", ".aiff", ".snd", ".ra", ".rm"};
  65.  
  66. static const int soundExtErrCodes[] = 
  67. {
  68.     AUDIO_MISSING_TEXT_WAV,
  69.     AUDIO_MISSING_TEXT_AU,
  70.     AUDIO_MISSING_TEXT_AIFF,
  71.     AUDIO_MISSING_TEXT_SND,
  72.     AUDIO_MISSING_TEXT_RA,
  73.     AUDIO_MISSING_TEXT_RM
  74. };
  75.  
  76. #define N_AUDIO_EXTS (sizeof(soundExtensions)/sizeof(ctmbstr))
  77.  
  78. /* List of possible media extensions */
  79. static const ctmbstr mediaExtensions[] = 
  80. {".mpg", ".mov", ".asx", ".avi", ".ivf", ".m1v", ".mmm", ".mp2v",
  81.  ".mpa", ".mpe", ".mpeg", ".ram", ".smi", ".smil", ".swf",
  82.  ".wm", ".wma", ".wmv"};
  83.  
  84. #define N_MEDIA_EXTS (sizeof(mediaExtensions)/sizeof(ctmbstr))
  85.  
  86. /* List of possible frame sources */
  87. static const ctmbstr frameExtensions[] =
  88. {".htm", ".html", ".shtm", ".shtml", ".cfm", ".cfml",
  89. ".asp", ".cgi", ".pl", ".smil"};
  90.  
  91. #define N_FRAME_EXTS (sizeof(frameExtensions)/sizeof(ctmbstr))
  92.  
  93. /* List of possible colour values */
  94. static const int colorValues[][3] =
  95. {
  96.   {  0,  0,  0},
  97.   {128,128,128},
  98.   {192,192,192},
  99.   {255,255,255},
  100.   {192,  0,  0},
  101.   {255,  0,  0},
  102.   {128,  0,128},
  103.   {255,  0,255},
  104.   {  0,128,  0},
  105.   {  0,255,  0},
  106.   {128,128,  0},
  107.   {255,255,  0},  
  108.   {  0,  0,128},
  109.   {  0,  0,255},
  110.   {  0,128,128},
  111.   {  0,255,255}
  112. };
  113.  
  114. #define N_COLOR_VALS (sizeof(colorValues)/(sizeof(int[3]))
  115.  
  116. /* These arrays are used to convert color names to their RGB values */
  117. static const ctmbstr colorNames[] =
  118. {
  119.   "black",
  120.   "silver",
  121.   "grey",
  122.   "white",
  123.   "maroon",
  124.   "red",
  125.   "purple",
  126.   "fuchsia",
  127.   "green",
  128.   "lime",
  129.   "olive",
  130.   "yellow", 
  131.   "navy",
  132.   "blue",
  133.   "teal",
  134.   "aqua"
  135. };
  136.  
  137. #define N_COLOR_NAMES (sizeof(colorNames)/sizeof(ctmbstr))
  138. #define N_COLORS N_COLOR_NAMES
  139.  
  140.  
  141. /* function prototypes */
  142. void InitAccessibilityChecks( TidyDocImpl* doc, int level123 );
  143. void FreeAccessibilityChecks( TidyDocImpl* doc );
  144.  
  145. static Bool GetRgb( ctmbstr color, int rgb[3] );
  146. static Bool CompareColors( int rgbBG[3], int rgbFG[3] );
  147. static int  ctox( tmbchar ch );
  148.  
  149. /*
  150. static void CheckMapAccess( TidyDocImpl* doc, Node* node, Node* front);
  151. static void GetMapLinks( TidyDocImpl* doc, Node* node, Node* front);
  152. static void CompareAnchorLinks( TidyDocImpl* doc, Node* front, int counter);
  153. static void FindMissingLinks( TidyDocImpl* doc, Node* node, int counter);
  154. */
  155. static void CheckFormControls( TidyDocImpl* doc, Node* node );
  156. static void MetaDataPresent( TidyDocImpl* doc, Node* node );
  157. static void CheckEmbed( TidyDocImpl* doc, Node* node );
  158. static void CheckListUsage( TidyDocImpl* doc, Node* node );
  159.  
  160. /*
  161.     GetFileExtension takes a path and returns the extension
  162.     portion of the path (if any).
  163. */
  164.  
  165. static void GetFileExtension( ctmbstr path, tmbchar *ext, uint maxExt )
  166. {
  167.     int i = tmbstrlen(path) - 1;
  168.     
  169.     ext[0] = '\0';
  170.     
  171.     do {
  172.         if ( path[i] == '/' || path[i] == '\\' )
  173.             break;
  174.         else if ( path[i] == '.' )
  175.         {
  176.             tmbstrncpy( ext, path+i, maxExt );
  177.             break;
  178.         }
  179.     } while ( --i > 0 );
  180. }
  181.  
  182. /************************************************************************
  183. * IsImage
  184. *
  185. * Checks if the given filename is an image file.
  186. * Returns 'yes' if it is, 'no' if it's not.
  187. ************************************************************************/
  188.  
  189. static Bool IsImage( ctmbstr iType )
  190. {
  191.     int i;
  192.  
  193.     /* Get the file extension */
  194.     tmbchar ext[20];
  195.     GetFileExtension( iType, ext, sizeof(ext) );
  196.  
  197.     /* Compare it to the array of known image file extensions */
  198.     for (i = 0; i < N_IMAGE_EXTS; i++)
  199.     {
  200.         if ( tmbstrcasecmp(ext, imageExtensions[i]) == 0 )
  201.             return yes;
  202.     }
  203.     
  204.     return no;
  205. }
  206.  
  207.  
  208. /***********************************************************************
  209. * IsSoundFile
  210. *
  211. * Checks if the given filename is a sound file.
  212. * Returns 'yes' if it is, 'no' if it's not.
  213. ***********************************************************************/
  214.  
  215. static int IsSoundFile( ctmbstr sType )
  216. {
  217.     int i;
  218.     tmbchar ext[ 20 ];
  219.     GetFileExtension( sType, ext, sizeof(ext) );
  220.  
  221.     for (i = 0; i < N_AUDIO_EXTS; i++)
  222.     {
  223.         if ( tmbstrcasecmp(ext, soundExtensions[i]) == 0 )
  224.             return soundExtErrCodes[i];
  225.     }
  226.     return 0;
  227. }
  228.  
  229.  
  230. /***********************************************************************
  231. * IsValidSrcExtension
  232. *
  233. * Checks if the 'SRC' value within the FRAME element is valid
  234. * The 'SRC' extension must end in ".htm", ".html", ".shtm", ".shtml", 
  235. * ".cfm", ".cfml", ".asp", ".cgi", ".pl", or ".smil"
  236. *
  237. * Returns yes if it is, returns no otherwise.
  238. ***********************************************************************/
  239.  
  240. static Bool IsValidSrcExtension( ctmbstr sType )
  241. {
  242.     int i;
  243.     tmbchar ext[20];
  244.     GetFileExtension( sType, ext, sizeof(ext) );
  245.  
  246.     for (i = 0; i < N_FRAME_EXTS; i++)
  247.     {
  248.         if ( tmbstrcasecmp(ext, frameExtensions[i]) == 0 )
  249.             return yes;
  250.     }
  251.     return no;
  252. }
  253.  
  254.  
  255. /*********************************************************************
  256. * IsValidMediaExtension
  257. *
  258. * Checks to warn the user that syncronized text equivalents are 
  259. * required if multimedia is used.
  260. *********************************************************************/
  261.  
  262. static Bool IsValidMediaExtension( ctmbstr sType )
  263. {
  264.     int i;
  265.     tmbchar ext[20];
  266.     GetFileExtension( sType, ext, sizeof(ext) );
  267.  
  268.     for (i = 0; i < N_MEDIA_EXTS; i++)
  269.     {
  270.         if ( tmbstrcasecmp(ext, mediaExtensions[i]) == 0 )
  271.             return yes;
  272.     }
  273.     return no;
  274. }
  275.  
  276.  
  277. /************************************************************************
  278. * IsWhitespace
  279. *
  280. * Checks if the given string is all whitespace.
  281. * Returns 'yes' if it is, 'no' if it's not.
  282. ************************************************************************/
  283.  
  284. static Bool IsWhitespace( ctmbstr pString )
  285. {
  286.     Bool isWht = yes;
  287.     ctmbstr cp;
  288.  
  289.     for ( cp = pString; isWht && cp && *cp; ++cp )
  290.     {
  291.         isWht = IsWhite( *cp );
  292.     }
  293.     return isWht;
  294. }
  295.  
  296. static Bool hasValue( AttVal* av )
  297. {
  298.   return ( av && ! IsWhitespace(av->value) );
  299. }
  300.  
  301. /***********************************************************************
  302. * IsPlaceholderAlt
  303. *  
  304. * Checks to see if there is an image and photo place holder contained
  305. * in the ALT text.
  306. *
  307. * Returns 'yes' if there is, 'no' if not.
  308. ***********************************************************************/
  309.  
  310. static Bool IsPlaceholderAlt( ctmbstr txt )
  311. {
  312.     return ( strstr(txt, "image") != NULL || 
  313.              strstr(txt, "photo") != NULL );
  314. }
  315.  
  316.  
  317. /***********************************************************************
  318. * IsPlaceholderTitle
  319. *  
  320. * Checks to see if there is an TITLE place holder contained
  321. * in the 'ALT' text.
  322. *
  323. * Returns 'yes' if there is, 'no' if not.
  324.  
  325. static Bool IsPlaceHolderTitle( ctmbstr txt )
  326. {
  327.     return ( strstr(txt, "title") != NULL );
  328. }
  329. ***********************************************************************/
  330.  
  331.  
  332. /***********************************************************************
  333. * IsPlaceHolderObject
  334. *  
  335. * Checks to see if there is an OBJECT place holder contained
  336. * in the 'ALT' text.
  337. *
  338. * Returns 'yes' if there is, 'no' if not.
  339. ***********************************************************************/
  340.  
  341. static Bool IsPlaceHolderObject( ctmbstr txt )
  342. {
  343.     return ( strstr(txt, "object") != NULL );
  344. }
  345.  
  346.  
  347. /**********************************************************
  348. * EndsWithBytes
  349. *
  350. * Checks to see if the ALT text ends with 'bytes'
  351. * Returns 'yes', if true, 'no' otherwise.
  352. **********************************************************/
  353.  
  354. static Bool EndsWithBytes( ctmbstr txt )
  355. {
  356.     uint len = tmbstrlen( txt );
  357.     return ( len >= 5 && strcmp(txt+len-5, "bytes") == 0 );
  358. }
  359.  
  360.  
  361. /*******************************************************
  362. * textFromOneNode
  363. *
  364. * Returns a list of characters contained within one
  365. * text node.
  366. *******************************************************/
  367.  
  368. static tmbstr textFromOneNode( TidyDocImpl* doc, Node* node )
  369. {
  370.     uint i;
  371.     int x = 0;
  372.     tmbstr txt = doc->access.text;
  373.     
  374.     if ( node )
  375.     {
  376.         /* Copy contents of a text node */
  377.         for (i = node->start; i < node->end; ++i, ++x )
  378.         {
  379.             txt[x] = doc->lexer->lexbuf[i];
  380.  
  381.             /* Check buffer overflow */
  382.             if ( x >= sizeof(doc->access.text)-1 )
  383.                 break;
  384.         }
  385.     }
  386.  
  387.     txt[x] = 0;
  388.     return txt;
  389. }
  390.  
  391.  
  392. /*********************************************************
  393. * getTextNode
  394. *
  395. * Locates text nodes within a container element.
  396. * Retrieves text that are found contained within 
  397. * text nodes, and concatenates the text.
  398. *********************************************************/
  399.     
  400. static void getTextNode( TidyDocImpl* doc, Node* node )
  401. {
  402.     tmbstr txtnod = doc->access.textNode;       
  403.     
  404.     /* 
  405.        Continues to traverse through container element until it no
  406.        longer contains any more contents 
  407.     */
  408.  
  409.     /* If the tag of the node is NULL, then grab the text within the node */
  410.     if ( node && node->type == TextNode )
  411.     {
  412.         uint i;
  413.  
  414.         /* Retrieves each character found within the text node */
  415.         for (i = node->start; i < node->end; i++)
  416.         {
  417.             /* The text must not exceed buffer */
  418.             if ( doc->access.counter >= TEXTBUF_SIZE-1 )
  419.                 return;
  420.  
  421.             txtnod[ doc->access.counter++ ] = doc->lexer->lexbuf[i];
  422.         }
  423.  
  424.         /* Traverses through the contents within a container element */
  425.         for ( node = node->content; node != NULL; node = node->next )
  426.             getTextNode( doc, node );
  427.     }   
  428. }
  429.  
  430.  
  431. /**********************************************************
  432. * getTextNodeClear
  433. *
  434. * Clears the current 'textNode' and reloads it with new
  435. * text.  The textNode must be cleared before use.
  436. **********************************************************/
  437.  
  438. static tmbstr getTextNodeClear( TidyDocImpl* doc, Node* node )
  439. {
  440.     /* Clears list */
  441.     ClearMemory( doc->access.textNode, TEXTBUF_SIZE );
  442.     doc->access.counter = 0;
  443.  
  444.     getTextNode( doc, node );
  445.     return doc->access.textNode;
  446. }
  447.     
  448.  
  449. /********************************************************
  450. * CheckColorAvailable
  451. *
  452. * Verify that information conveyed with color is 
  453. * available without color.
  454. ********************************************************/
  455.  
  456. static void CheckColorAvailable( TidyDocImpl* doc, Node* node )
  457. {
  458.     if ( doc->access.PRIORITYCHK == 1 ||
  459.          doc->access.PRIORITYCHK == 2 ||
  460.          doc->access.PRIORITYCHK == 3 )
  461.     {
  462.         if ( nodeIsIMG(node) )
  463.             ReportAccessWarning( doc, node, INFORMATION_NOT_CONVEYED_IMAGE );
  464.  
  465.         else if ( nodeIsAPPLET(node) )
  466.             ReportAccessWarning( doc, node, INFORMATION_NOT_CONVEYED_APPLET );
  467.  
  468.         else if ( nodeIsOBJECT(node) )
  469.             ReportAccessWarning( doc, node, INFORMATION_NOT_CONVEYED_OBJECT );
  470.  
  471.         else if ( nodeIsSCRIPT(node) )
  472.             ReportAccessWarning( doc, node, INFORMATION_NOT_CONVEYED_SCRIPT );
  473.  
  474.         else if ( nodeIsINPUT(node) )
  475.             ReportAccessWarning( doc, node, INFORMATION_NOT_CONVEYED_INPUT );
  476.     }
  477. }
  478.  
  479. /*********************************************************************
  480. * CheckColorContrast
  481. *
  482. * Checks elements for color contrast.  Must have valid contrast for
  483. * valid visibility.
  484. *
  485. * This logic is extremely fragile as it does not recognize
  486. * the fact that color is inherited by many components and
  487. * that BG and FG colors are often set separately.  E.g. the
  488. * background color may be set by for the body or a table 
  489. * or a cell.  The foreground color may be set by any text
  490. * element (p, h1, h2, input, textarea), either explicitly
  491. * or by style.  Ergo, this test will not handle most real
  492. * world cases.  It's a start, however.
  493. *********************************************************************/
  494.  
  495. static void CheckColorContrast( TidyDocImpl* doc, Node* node )
  496. {
  497.     int rgbBG[3] = {255,255,255};   /* Black text on white BG */
  498.  
  499.     if ( doc->access.PRIORITYCHK == 3 )
  500.     {
  501.         Bool gotBG = yes;
  502.         AttVal* av;
  503.  
  504.         /* Check for 'BGCOLOR' first to compare with other color attributes */
  505.         for ( av = node->attributes; av; av = av->next )
  506.         {            
  507.             if ( attrIsBGCOLOR(av) )
  508.             {
  509.                 if ( hasValue(av) )
  510.                     gotBG = GetRgb( av->value, rgbBG );
  511.             }
  512.         }
  513.         
  514.         /* 
  515.            Search for COLOR attributes to compare with background color
  516.            Must have valid colour contrast
  517.         */
  518.         for ( av = node->attributes; gotBG && av != NULL; av = av->next )
  519.         {
  520.             uint errcode = 0;
  521.             if ( attrIsTEXT(av) )
  522.                 errcode = COLOR_CONTRAST_TEXT;
  523.             else if ( attrIsLINK(av) )
  524.                 errcode = COLOR_CONTRAST_LINK;
  525.             else if ( attrIsALINK(av) )
  526.                 errcode = COLOR_CONTRAST_ACTIVE_LINK;
  527.             else if ( attrIsVLINK(av) )
  528.                 errcode = COLOR_CONTRAST_VISITED_LINK;
  529.  
  530.             if ( errcode && hasValue(av) )
  531.             {
  532.                 int rgbFG[3] = {0, 0, 0};  /* Black text */
  533.  
  534.                 if ( GetRgb(av->value, rgbFG) &&
  535.                      !CompareColors(rgbBG, rgbFG) )
  536.                 {
  537.                     ReportAccessWarning( doc, node, errcode );
  538.                 }
  539.             }
  540.         }
  541.     }
  542. }
  543.  
  544.  
  545. /**************************************************************
  546. * CompareColors
  547. *
  548. * Compares two RGB colors for good contrast.
  549. **************************************************************/
  550. static int minmax( int i1, int i2 )
  551. {
  552.    return MAX(i1, i2) - MIN(i1,i2);
  553. }
  554. static int brightness( int rgb[3] )
  555. {
  556.    return ((rgb[0]*299) + (rgb[1]*587) + (rgb[2]*114)) / 1000;
  557. }
  558.  
  559. static Bool CompareColors( int rgbBG[3], int rgbFG[3] )
  560. {
  561.     int brightBG = brightness( rgbBG );
  562.     int brightFG = brightness( rgbFG );
  563.  
  564.     int diffBright = minmax( brightBG, brightFG );
  565.  
  566.     int diffColor = minmax( rgbBG[0], rgbFG[0] )
  567.                   + minmax( rgbBG[1], rgbFG[1] )
  568.                   + minmax( rgbBG[2], rgbFG[2] );
  569.  
  570.     return ( diffBright > 180 &&
  571.              diffColor > 500 );
  572. }
  573.  
  574.  
  575. /*********************************************************************
  576. * GetRgb
  577. *
  578. * Gets the red, green and blue values for this attribute for the 
  579. * background.
  580. *
  581. * Example: If attribute is BGCOLOR="#121005" then red = 18, green = 16,
  582. * blue = 5.
  583. *********************************************************************/
  584.  
  585. static Bool GetRgb( ctmbstr color, int rgb[] )
  586. {
  587.     int x;
  588.  
  589.     /* Check if we have a color name */
  590.     for (x = 0; x < N_COLORS; x++)
  591.     {
  592.         if ( strstr(colorNames[x], color) != NULL )
  593.         {
  594.             rgb[0] = colorValues[x][0];
  595.             rgb[1] = colorValues[x][1];
  596.             rgb[2] = colorValues[x][2];
  597.             return yes;
  598.         }
  599.     }
  600.  
  601.     /*
  602.        No color name so must be hex values 
  603.        Is this a number in hexadecimal format?
  604.     */
  605.     
  606.     /* Must be 7 characters in the RGB value (including '#') */
  607.     if ( tmbstrlen(color) == 7 && color[0] == '#' )
  608.     {
  609.         rgb[0] = (ctox(color[1]) * 16) + ctox(color[2]);
  610.         rgb[1] = (ctox(color[3]) * 16) + ctox(color[4]);
  611.         rgb[2] = (ctox(color[5]) * 16) + ctox(color[6]);
  612.         return yes;
  613.     }
  614.     return no;
  615.  
  616.  
  617.  
  618. /*******************************************************************
  619. * ctox
  620. *
  621. * Converts a character to a number.
  622. * Example: if given character is 'A' then returns 10.
  623. *
  624. * Returns the number that the character represents. Returns -1 if not a
  625. * valid number.
  626. *******************************************************************/
  627.  
  628. static int ctox( tmbchar ch )
  629. {
  630.     if ( ch >= '0' && ch <= '9' )
  631.     {
  632.          return ch - '0';
  633.     }
  634.     else if ( ch >= 'a' && ch <= 'f' )
  635.     {
  636.         return ch - 'a' + 10;
  637.     }
  638.     else if ( ch >= 'A' && ch <= 'F' )
  639.     {
  640.         return ch - 'A' + 10;
  641.     }
  642.     return -1;
  643. }
  644.  
  645.  
  646. /***********************************************************
  647. * CheckImage
  648. *
  649. * Checks all image attributes for specific elements to
  650. * check for validity of the values contained within
  651. * the attributes.  An appropriate warning message is displayed
  652. * to indicate the error.  
  653. ***********************************************************/
  654.  
  655. static void CheckImage( TidyDocImpl* doc, Node* node )
  656. {
  657.     Bool HasAlt = no;
  658.     Bool HasIsMap = no;
  659.     Bool HasLongDesc = no;
  660.     Bool HasDLINK = no;
  661.     Bool HasValidHeight = no;
  662.     Bool HasValidWidthBullet = no;
  663.     Bool HasValidWidthHR = no; 
  664.     Bool HasTriggeredMissingAlt = no;
  665.     Bool HasTriggeredMissingLongDesc = no;
  666.  
  667.     AttVal* av;
  668.     tmbstr word;
  669.                 
  670.     if ((doc->access.PRIORITYCHK == 1)||
  671.         (doc->access.PRIORITYCHK == 2)||
  672.         (doc->access.PRIORITYCHK == 3))
  673.     {
  674.         /* Checks all image attributes for invalid values within attributes */
  675.         for (av = node->attributes; av != NULL; av = av->next)
  676.         {
  677.             /* 
  678.                Checks for valid ALT attribute.
  679.                The length of the alt text must be less than 150 characters 
  680.                long.
  681.             */
  682.             if ( attrIsALT(av) )
  683.             {
  684.                 if (av->value != NULL) 
  685.                 {
  686.                     if ((tmbstrlen(av->value) < 150) &&
  687.                         (IsPlaceholderAlt (av->value) == no) &&
  688.                         (IsPlaceHolderObject (av->value) == no) &&
  689.                         (EndsWithBytes (av->value) == no) &&
  690.                         (IsImage (av->value) == no))
  691.                     {
  692.                         HasAlt = yes;
  693.                     }
  694.  
  695.                     else if (tmbstrlen (av->value) > 150)
  696.                     {
  697.                         HasAlt = yes;
  698.                         ReportAccessWarning( doc, node, IMG_ALT_SUSPICIOUS_TOO_LONG );
  699.                     }
  700.  
  701.                     else if (IsImage (av->value) == yes)
  702.                     {
  703.                         HasAlt = yes;
  704.                         ReportAccessWarning( doc, node, IMG_ALT_SUSPICIOUS_FILENAME);
  705.                     }
  706.             
  707.                     else if (IsPlaceholderAlt (av->value) == yes)
  708.                     {
  709.                         HasAlt = yes;
  710.                         ReportAccessWarning( doc, node, IMG_ALT_SUSPICIOUS_PLACEHOLDER);
  711.                     }
  712.  
  713.                     else if (EndsWithBytes (av->value) == yes)
  714.                     {
  715.                         HasAlt = yes;
  716.                         ReportAccessWarning( doc, node, IMG_ALT_SUSPICIOUS_FILE_SIZE);
  717.                     }
  718.                 }
  719.             }
  720.  
  721.             /* 
  722.                Checks for width values of 'bullets' and 'horizontal
  723.                rules' for validity.
  724.  
  725.                Valid pixel width for 'bullets' must be < 30, and > 150 for
  726.                horizontal rules.
  727.             */
  728.             else if ( attrIsWIDTH(av) )
  729.             {
  730.                 /* Longdesc attribute needed if width attribute is not present. */
  731.                 if ( hasValue(av) )
  732.                 {
  733.                     int width = atoi( av->value );
  734.                     if ( width < 30 )
  735.                         HasValidWidthBullet = yes;
  736.  
  737.                     if ( width > 150 )
  738.                         HasValidWidthHR = yes;
  739.                 }
  740.             }
  741.  
  742.             /* 
  743.                Checks for height values of 'bullets' and horizontal
  744.                rules for validity.
  745.  
  746.                Valid pixel height for 'bullets' and horizontal rules 
  747.                mustt be < 30.
  748.             */
  749.             else if ( attrIsHEIGHT(av) )
  750.             {
  751.                 /* Longdesc attribute needed if height attribute not present. */
  752.                 if ( hasValue(av) && atoi(av->value) < 30 )
  753.                     HasValidHeight = yes;
  754.             }
  755.  
  756.             /* 
  757.                Checks for longdesc and determines validity.  
  758.                The length of the 'longdesc' must be > 1
  759.             */
  760.             else if ( attrIsLONGDESC(av) )
  761.             {
  762.                 if ( hasValue(av) && tmbstrlen(av->value) > 1 )
  763.                     HasLongDesc = yes;
  764.               }
  765.  
  766.             /* 
  767.                Checks for 'USEMAP' attribute.  Ensures that
  768.                text links are provided for client-side image maps
  769.             */
  770.             else if ( attrIsUSEMAP(av) )
  771.             {
  772.                 if ( hasValue(av) )
  773.                     doc->access.HasUseMap = yes;
  774.             }    
  775.  
  776.             else if ( attrIsISMAP(av) )
  777.             {
  778.                 HasIsMap = yes;
  779.             }
  780.         }    
  781.         
  782.         
  783.         /* 
  784.             Check to see if a dLINK is present.  The ANCHOR element must
  785.             be present following the IMG element.  The text found between 
  786.             the ANCHOR tags must be < 6 characters long, and must contain
  787.             the letter 'd'.
  788.         */
  789.         if ( nodeIsA(node->next) )
  790.         {
  791.             node = node->next;
  792.             
  793.             /* 
  794.                 Node following the anchor must be a text node
  795.                 for dLINK to exist 
  796.             */
  797.  
  798.             if(node->content != NULL && (node->content)->tag == NULL)
  799.             {
  800.                 /* Number of characters found within the text node */
  801.                 word = textFromOneNode( doc, node->content);
  802.                     
  803.                 if ((strcmp(word,"d") == 0)||
  804.                     (strcmp(word,"D") == 0))
  805.                 {
  806.                     HasDLINK = yes;
  807.                 }
  808.             }
  809.         }
  810.                     
  811.         /*
  812.             Special case check for dLINK.  This will occur if there is 
  813.             whitespace between the <img> and <a> elements.  Ignores 
  814.             whitespace and continues check for dLINK.
  815.         */
  816.         
  817.         if ( node->next && !node->next->tag )
  818.         {
  819.             node = node->next;
  820.  
  821.             if ( nodeIsA(node->next) )
  822.             {
  823.                 node = node->next;
  824.  
  825.                 /* 
  826.                     Node following the ANCHOR must be a text node
  827.                     for dLINK to exist 
  828.                 */
  829.                 if(node->content != NULL && node->content->tag == NULL)
  830.                 {
  831.                     /* Number of characters found within the text node */
  832.                     word = textFromOneNode( doc, node->content );
  833.  
  834.                     if ((strcmp(word, "d") == 0)||
  835.                         (strcmp(word, "D") == 0))
  836.                     {
  837.                         HasDLINK = yes;
  838.                     }
  839.                 }
  840.             }    
  841.         }
  842.  
  843.         if ((HasAlt == no)&&
  844.             (HasValidWidthBullet == yes)&&
  845.             (HasValidHeight == yes))
  846.         {
  847.             ReportAccessError( doc, node, IMG_MISSING_ALT_BULLET);
  848.             HasTriggeredMissingAlt = yes;
  849.         }
  850.  
  851.         if ((HasAlt == no)&&
  852.             (HasValidWidthHR == yes)&&
  853.             (HasValidHeight == yes))
  854.         {
  855.             ReportAccessError( doc, node, IMG_MISSING_ALT_H_RULE);
  856.             HasTriggeredMissingAlt = yes;
  857.         }
  858.         
  859.         if (HasTriggeredMissingAlt == no)
  860.         {
  861.             if (HasAlt == no)
  862.             {
  863.                 ReportAccessError( doc, node, IMG_MISSING_ALT);
  864.             }
  865.         }
  866.  
  867.         if ((HasLongDesc == no)&&
  868.             (HasValidHeight ==yes)&&
  869.             (HasValidWidthHR == yes)||
  870.             (HasValidWidthBullet == yes))
  871.         {
  872.             HasTriggeredMissingLongDesc = yes;
  873.             ReportAccessWarning( doc, node, LONGDESC_NOT_REQUIRED);
  874.         }
  875.  
  876.         if (HasTriggeredMissingLongDesc == no)
  877.         {
  878.             if ((HasDLINK == yes)&&
  879.                 (HasLongDesc == no))
  880.             {
  881.                 ReportAccessWarning( doc, node, IMG_MISSING_LONGDESC);
  882.             }
  883.  
  884.             if ((HasLongDesc == yes)&&
  885.                 (HasDLINK == no))
  886.             {
  887.                 ReportAccessWarning( doc, node, IMG_MISSING_DLINK);
  888.             }
  889.             
  890.             if ((HasLongDesc == no)&&
  891.                 (HasDLINK == no))
  892.             {
  893.                 ReportAccessWarning( doc, node, IMG_MISSING_LONGDESC_DLINK);
  894.             }
  895.         }
  896.         
  897.         if (HasIsMap == yes)
  898.         {
  899.             ReportAccessError( doc, node, IMAGE_MAP_SERVER_SIDE_REQUIRES_CONVERSION);
  900.  
  901.             ReportAccessWarning( doc, node, IMG_MAP_SERVER_REQUIRES_TEXT_LINKS);
  902.         }
  903.     }
  904. }
  905.  
  906.  
  907. /***********************************************************
  908. * CheckApplet
  909. *
  910. * Checks APPLET element to check for validity pertaining 
  911. * the 'ALT' attribute.  An appropriate warning message is 
  912. * displayed  to indicate the error. An appropriate warning 
  913. * message is displayed to indicate the error.  If no 'ALT'
  914. * text is present, then there must be alternate content
  915. * within the APPLET element.
  916. ***********************************************************/
  917.  
  918. static void CheckApplet( TidyDocImpl* doc, Node* node )
  919. {
  920.     Bool HasAlt = no;
  921.     Bool HasDescription = no;
  922.  
  923.     AttVal* av;
  924.     tmbstr word = NULL;
  925.         
  926.     if ((doc->access.PRIORITYCHK == 1)||
  927.         (doc->access.PRIORITYCHK == 2)||
  928.         (doc->access.PRIORITYCHK == 3))
  929.     {
  930.         /* Checks for attributes within the APPLET element */
  931.         for (av = node->attributes; av != NULL; av = av->next)
  932.         {
  933.             /*
  934.                Checks for valid ALT attribute.
  935.                The length of the alt text must be > 4 characters in length
  936.                but must be < 150 characters long.
  937.             */
  938.  
  939.             if ( attrIsALT(av) )
  940.             {
  941.                 if (av->value != NULL)
  942.                 {
  943.                     HasAlt = yes;
  944.                 }
  945.             }
  946.         }
  947.  
  948.         if (HasAlt == no)
  949.         {
  950.             /* Must have alternate text representation for that element */
  951.             if (node->content != NULL) 
  952.             {
  953.                 if ( node->content->tag == NULL )
  954.                     word = textFromOneNode( doc, node->content);
  955.  
  956.                 if ( node->content->content != NULL &&
  957.                      node->content->content->tag == NULL )
  958.                 {
  959.                     word = textFromOneNode( doc, node->content->content);
  960.                 }
  961.                 
  962.                 if ( word != NULL && !IsWhitespace(word) )
  963.                     HasDescription = yes;
  964.             }
  965.         }
  966.  
  967.         if ( !HasDescription && !HasAlt )
  968.         {
  969.             ReportAccessError( doc, node, APPLET_MISSING_ALT );
  970.         }
  971.     }
  972. }
  973.  
  974.  
  975. /*******************************************************************
  976. * CheckObject
  977. *
  978. * Checks to verify whether the OBJECT element contains
  979. * 'ALT' text, and to see that the sound file selected is 
  980. * of a valid sound file type.  OBJECT must have an alternate text 
  981. * representation.
  982. *******************************************************************/
  983.  
  984. static void CheckObject( TidyDocImpl* doc, Node* node )
  985. {
  986.     tmbstr word = NULL;
  987.  
  988.     Bool HasAlt = no;
  989.     Bool HasDescription = no;
  990.  
  991.     if ( doc->access.PRIORITYCHK == 1 ||
  992.          doc->access.PRIORITYCHK == 2 ||
  993.          doc->access.PRIORITYCHK == 3 )
  994.     {
  995.         if ( node->content != NULL)
  996.         {
  997.             if ( node->content->type != TextNode )
  998.             {
  999.                 Node* tnode = node->content;
  1000.                 AttVal* av;
  1001.  
  1002.                 for ( av=tnode->attributes; av; av = av->next )
  1003.                 {
  1004.                     if ( attrIsALT(av) )
  1005.                     {
  1006.                         HasAlt = yes;
  1007.                         break;
  1008.                     }
  1009.                 }
  1010.             }
  1011.  
  1012.             /* Must have alternate text representation for that element */
  1013.             if ( !HasAlt )
  1014.             {
  1015.                 if ( node->content->type == TextNode )
  1016.                     word = textFromOneNode( doc, node->content );
  1017.  
  1018.                 if ( word == NULL && 
  1019.                      node->content->content != NULL &&
  1020.                      node->content->content->type == TextNode )
  1021.                 {
  1022.                     word = textFromOneNode( doc, node->content->content );
  1023.                 }
  1024.                     
  1025.                 if ( word != NULL && !IsWhitespace(word) )
  1026.                     HasDescription = yes;
  1027.             }
  1028.         }
  1029.  
  1030.         if ( !HasAlt && !HasDescription )
  1031.         {
  1032.             ReportAccessError( doc, node, OBJECT_MISSING_ALT );
  1033.         }
  1034.     }
  1035. }
  1036.  
  1037.  
  1038. /***************************************************************
  1039. * CheckMissingStyleSheets
  1040. *
  1041. * Ensures that stylesheets are used to control the presentation.
  1042. ***************************************************************/
  1043.  
  1044. static Bool CheckMissingStyleSheets( TidyDocImpl* doc, Node* node )
  1045. {
  1046.     AttVal* av;
  1047.     Node* content;
  1048.     Bool sspresent = no;
  1049.  
  1050.     for ( content = node->content;
  1051.           !sspresent && content != NULL;
  1052.           content = content->next )
  1053.     {
  1054.         sspresent = ( nodeIsLINK(content)  ||
  1055.                       nodeIsSTYLE(content) ||
  1056.                       nodeIsFONT(content)  ||
  1057.                       nodeIsBASEFONT(content) );
  1058.  
  1059.         for ( av = content->attributes;
  1060.               !sspresent && av != NULL;
  1061.               av = av->next )
  1062.         {
  1063.             sspresent = ( attrIsSTYLE(av) || attrIsTEXT(av)  ||
  1064.                           attrIsVLINK(av) || attrIsALINK(av) ||
  1065.                           attrIsLINK(av) );
  1066.  
  1067.             if ( !sspresent && attrIsREL(av) )
  1068.             {
  1069.                 sspresent = AttrValueIs(av, "stylesheet");
  1070.             }
  1071.         }
  1072.  
  1073.         if ( ! sspresent )
  1074.             sspresent = CheckMissingStyleSheets( doc, content );
  1075.     }
  1076.     return sspresent;
  1077. }
  1078.  
  1079.  
  1080. /*******************************************************************
  1081. * CheckFrame
  1082. *
  1083. * Checks if the URL is valid and to check if a 'LONGDESC' is needed
  1084. * within the FRAME element.  If a 'LONGDESC' is needed, the value must 
  1085. * be valid. The URL must end with the file extension, htm, or html. 
  1086. * Also, checks to ensure that the 'SRC' and 'TITLE' values are valid. 
  1087. *******************************************************************/
  1088.  
  1089. static void CheckFrame( TidyDocImpl* doc, Node* node )
  1090. {
  1091.     Bool HasTitle = no;
  1092.     AttVal* av;
  1093.  
  1094.     doc->access.numFrames++;
  1095.  
  1096.     if ( doc->access.PRIORITYCHK == 1 ||
  1097.          doc->access.PRIORITYCHK == 2 ||
  1098.          doc->access.PRIORITYCHK == 3 )
  1099.     {
  1100.         /* Checks for attributes within the FRAME element */
  1101.         for (av = node->attributes; av != NULL; av = av->next)
  1102.         {
  1103.             /* Checks if 'LONGDESC' value is valid only if present */
  1104.             if ( attrIsLONGDESC(av) )
  1105.             {
  1106.                 if ( hasValue(av) && tmbstrlen(av->value) > 1 )
  1107.                 {
  1108.                     doc->access.HasCheckedLongDesc++;
  1109.                 }
  1110.             }
  1111.  
  1112.             /* Checks for valid 'SRC' value within the frame element */
  1113.             else if ( attrIsSRC(av) )
  1114.             {
  1115.                 if ( hasValue(av) && !IsValidSrcExtension(av->value) )
  1116.                 {
  1117.                     ReportAccessError( doc, node, FRAME_SRC_INVALID );
  1118.                 }
  1119.             }
  1120.  
  1121.             /* Checks for valid 'TITLE' value within frame element */
  1122.             else if ( attrIsTITLE(av) )
  1123.             {
  1124.                 if ( hasValue(av) )
  1125.                     HasTitle = yes;
  1126.  
  1127.                 if ( !HasTitle )
  1128.                 {
  1129.                     if ( av->value == NULL || tmbstrlen(av->value) == 0 )
  1130.                     {
  1131.                         HasTitle = yes;
  1132.                         ReportAccessError( doc, node, FRAME_TITLE_INVALID_NULL);
  1133.                     }
  1134.                     else
  1135.                     {
  1136.                         if ( IsWhitespace(av->value) && tmbstrlen(av->value) > 0 )
  1137.                         {
  1138.                             HasTitle = yes;
  1139.                             ReportAccessError( doc, node, FRAME_TITLE_INVALID_SPACES );
  1140.                         }
  1141.                     }
  1142.                 }
  1143.             }
  1144.         }
  1145.  
  1146.         if ( !HasTitle )
  1147.         {
  1148.             ReportAccessError( doc, node, FRAME_MISSING_TITLE);
  1149.         }
  1150.  
  1151.         if ( doc->access.numFrames==3 && doc->access.HasCheckedLongDesc<3 )
  1152.         {
  1153.             doc->access.numFrames = 0;
  1154.             ReportAccessWarning( doc, node, FRAME_MISSING_LONGDESC );
  1155.         }
  1156.     }
  1157. }
  1158.  
  1159.  
  1160. /****************************************************************
  1161. * CheckIFrame
  1162. *
  1163. * Checks if 'SRC' value is valid.  Must end in appropriate
  1164. * file extension.
  1165. ****************************************************************/
  1166.  
  1167. static void CheckIFrame( TidyDocImpl* doc, Node* node )
  1168. {
  1169.     if ( doc->access.PRIORITYCHK == 1 ||
  1170.          doc->access.PRIORITYCHK == 2 ||
  1171.          doc->access.PRIORITYCHK == 3 )
  1172.     {
  1173.         /* Checks for valid 'SRC' value within the IFRAME element */
  1174.         AttVal* av = attrGetSRC( node );
  1175.         if ( hasValue(av) )
  1176.         {
  1177.             if ( !IsValidSrcExtension(av->value) )
  1178.                 ReportAccessError( doc, node, FRAME_SRC_INVALID );
  1179.         }
  1180.     }
  1181. }
  1182.  
  1183.  
  1184. /**********************************************************************
  1185. * CheckAnchorAccess
  1186. *
  1187. * Checks that the sound file is valid, and to ensure that
  1188. * text transcript is present describing the 'HREF' within the 
  1189. * ANCHOR element.  Also checks to see ensure that the 'TARGET' attribute
  1190. * (if it exists) is not NULL and does not contain '_new' or '_blank'.
  1191. **********************************************************************/
  1192.  
  1193. static void CheckAnchorAccess( TidyDocImpl* doc, Node* node )
  1194. {
  1195.     AttVal* av;
  1196.     tmbstr word = NULL;
  1197.     int checked = 0;
  1198.     Bool HasDescription = no;
  1199.     Bool HasTriggeredLink = no;
  1200.  
  1201.     /* Checks for attributes within the ANCHOR element */
  1202.     for ( av = node->attributes; av != NULL; av = av->next )
  1203.     {
  1204.         if ( doc->access.PRIORITYCHK == 1 ||
  1205.              doc->access.PRIORITYCHK == 2 ||
  1206.              doc->access.PRIORITYCHK == 3 )
  1207.         {
  1208.             /* Must be of valid sound file type */
  1209.             if ( attrIsHREF(av) )
  1210.             {
  1211.                 if ( hasValue(av) )
  1212.                 {
  1213.                     tmbchar ext[ 20 ];
  1214.                     GetFileExtension (av->value, ext, sizeof(ext) );
  1215.  
  1216.                     /* Checks to see if multimedia is used */
  1217.                     if ( IsValidMediaExtension(av->value) )
  1218.                     {
  1219.                         ReportAccessError( doc, node, MULTIMEDIA_REQUIRES_TEXT );
  1220.                     }
  1221.             
  1222.                     /* 
  1223.                         Checks for validity of sound file, and checks to see if 
  1224.                         the file is described within the document, or by a link
  1225.                         that is present which gives the description.
  1226.                     */
  1227.                     if ( tmbstrlen(ext) < 6 && tmbstrlen(ext) > 0 )
  1228.                     {
  1229.                         int errcode = IsSoundFile( av->value );
  1230.                         if ( errcode )
  1231.                         {
  1232.                             if (node->next != NULL)
  1233.                             {
  1234.                                 if (node->next->tag == NULL)
  1235.                                 {
  1236.                                     word = textFromOneNode( doc, node->next);
  1237.                                 
  1238.                                     /* Must contain at least one letter in the text */
  1239.                                     if (IsWhitespace (word) == no)
  1240.                                     {
  1241.                                         HasDescription = yes;
  1242.                                     }
  1243.                                 }
  1244.                             }
  1245.  
  1246.                             /* Must contain text description of sound file */
  1247.                             if ( !HasDescription )
  1248.                             {
  1249.                                 ReportAccessError( doc, node, errcode );
  1250.                             }
  1251.                         }
  1252.                     }
  1253.                 }
  1254.             }
  1255.         }
  1256.  
  1257.         if ( doc->access.PRIORITYCHK == 2 ||
  1258.              doc->access.PRIORITYCHK == 3 )
  1259.         {
  1260.             /* Checks 'TARGET' attribute for validity if it exists */
  1261.             if ( attrIsTARGET(av) )
  1262.             {
  1263.                 checked = 1;
  1264.  
  1265.                 if (AttrValueIs(av, "_new"))
  1266.                 {
  1267.                     ReportAccessWarning( doc, node, NEW_WINDOWS_REQUIRE_WARNING_NEW);
  1268.                 }
  1269.                 else if (AttrValueIs(av, "_blank"))
  1270.                 {
  1271.                     ReportAccessWarning( doc, node, NEW_WINDOWS_REQUIRE_WARNING_BLANK);
  1272.                 }
  1273.             }
  1274.         }
  1275.     }
  1276.     
  1277.     if ( doc->access.PRIORITYCHK == 2 ||
  1278.          doc->access.PRIORITYCHK == 3 )
  1279.     {
  1280.         if ((node->content != NULL)&&
  1281.             (node->content->tag == NULL))
  1282.         {
  1283.             word = textFromOneNode( doc, node->content);
  1284.  
  1285.             if ((word != NULL)&&
  1286.                 (IsWhitespace (word) == no))
  1287.             {
  1288.                 if (strcmp (word, "more") == 0)
  1289.                 {
  1290.                     HasTriggeredLink = yes;
  1291.                     ReportAccessWarning( doc, node, LINK_TEXT_NOT_MEANINGFUL_MORE);
  1292.                 }
  1293.  
  1294.                 if (strcmp (word, "follow this") == 0)
  1295.                 {
  1296.                     ReportAccessWarning( doc, node, LINK_TEXT_NOT_MEANINGFUL_FOLLOW_THIS);
  1297.                 }
  1298.  
  1299.                 if (strcmp (word, "click here") == 0)
  1300.                 {
  1301.                     ReportAccessWarning( doc, node, LINK_TEXT_NOT_MEANINGFUL_CLICK_HERE);
  1302.                 }
  1303.  
  1304.                 if (HasTriggeredLink == no)
  1305.                 {
  1306.                     if (tmbstrlen (word) < 6)
  1307.                     {
  1308.                         ReportAccessWarning( doc, node, LINK_TEXT_NOT_MEANINGFUL);
  1309.                     }
  1310.                 }
  1311.  
  1312.                 if (tmbstrlen (word) > 60)
  1313.                 {
  1314.                     ReportAccessWarning( doc, node, LINK_TEXT_TOO_LONG);
  1315.                 }
  1316.  
  1317.             }
  1318.         }
  1319.         
  1320.         if (node->content == NULL)
  1321.         {
  1322.             ReportAccessWarning( doc, node, LINK_TEXT_MISSING);
  1323.         }
  1324.     }
  1325. }
  1326.  
  1327.  
  1328. /************************************************************
  1329. * CheckArea
  1330. *
  1331. * Checks attributes within the AREA element to 
  1332. * determine if the 'ALT' text and 'HREF' values are valid.
  1333. * Also checks to see ensure that the 'TARGET' attribute
  1334. * (if it exists) is not NULL and does not contain '_new' 
  1335. * or '_blank'.
  1336. ************************************************************/
  1337.  
  1338. static void CheckArea( TidyDocImpl* doc, Node* node )
  1339. {
  1340.     Bool HasAlt = no;
  1341.     AttVal* av;
  1342.  
  1343.     /* Checks all attributes within the AREA element */
  1344.     for (av = node->attributes; av != NULL; av = av->next)
  1345.     {
  1346.         if ((doc->access.PRIORITYCHK == 1)||
  1347.             (doc->access.PRIORITYCHK == 2)||
  1348.             (doc->access.PRIORITYCHK == 3))
  1349.         {
  1350.             /*
  1351.               Checks for valid ALT attribute.
  1352.               The length of the alt text must be > 4 characters long
  1353.               but must be less than 150 characters long.
  1354.             */
  1355.                 
  1356.             if ( attrIsALT(av) )
  1357.             {
  1358.                 /* The check for validity */
  1359.                 if (av->value != NULL) 
  1360.                 {
  1361.                     HasAlt = yes;
  1362.                 }
  1363.             }
  1364.         }
  1365.  
  1366.         if ((doc->access.PRIORITYCHK == 2)||
  1367.             (doc->access.PRIORITYCHK == 3))
  1368.         {
  1369.             if ( attrIsTARGET(av) )
  1370.             {
  1371.                 if (AttrValueIs(av, "_new"))
  1372.                 {
  1373.                     ReportAccessWarning( doc, node, NEW_WINDOWS_REQUIRE_WARNING_NEW);
  1374.                 }
  1375.                 else if (AttrValueIs(av, "_blank"))
  1376.                 {
  1377.                     ReportAccessWarning( doc, node, NEW_WINDOWS_REQUIRE_WARNING_BLANK);
  1378.                 }
  1379.             }
  1380.         }
  1381.     }
  1382.  
  1383.     if ((doc->access.PRIORITYCHK == 1)||
  1384.         (doc->access.PRIORITYCHK == 2)||
  1385.         (doc->access.PRIORITYCHK == 3))
  1386.     {
  1387.         /* AREA must contain alt text */
  1388.         if (HasAlt == no)
  1389.         {
  1390.             ReportAccessError( doc, node, AREA_MISSING_ALT);
  1391.         }    
  1392.     }
  1393. }
  1394.  
  1395.  
  1396. /***************************************************
  1397. * CheckScript
  1398. *
  1399. * Checks the SCRIPT element to ensure that a
  1400. * NOSCRIPT section follows the SCRIPT.  
  1401. ***************************************************/
  1402.  
  1403. static void CheckScriptAcc( TidyDocImpl* doc, Node* node )
  1404. {
  1405.     if ( doc->access.PRIORITYCHK == 1 ||
  1406.          doc->access.PRIORITYCHK == 2 ||
  1407.          doc->access.PRIORITYCHK == 3 )
  1408.     {
  1409.         /* NOSCRIPT element must appear immediately following SCRIPT element */
  1410.         if ( node->next == NULL || !nodeIsNOSCRIPT(node->next) )
  1411.         {
  1412.             ReportAccessError( doc, node, SCRIPT_MISSING_NOSCRIPT);
  1413.         }
  1414.     }
  1415. }
  1416.  
  1417.  
  1418. /**********************************************************
  1419. * CheckRows
  1420. *
  1421. * Check to see that each table has a row of headers if
  1422. * a column of columns doesn't exist. 
  1423. **********************************************************/
  1424.  
  1425. static void CheckRows( TidyDocImpl* doc, Node* node )
  1426. {
  1427.     int numTR = 0;
  1428.     int numValidTH = 0;
  1429.     tmbstr word;
  1430.     
  1431.     doc->access.CheckedHeaders++;
  1432.  
  1433.     for(;;)
  1434.     {
  1435.         if (node == NULL)
  1436.         {
  1437.             break;
  1438.         }
  1439.  
  1440.         else 
  1441.         {
  1442.             numTR++;
  1443.  
  1444.             if ( nodeIsTH(node) )
  1445.             {
  1446.                 doc->access.HasTH = yes;
  1447.             
  1448.                 if ( node->content && nodeIsText(node->content->content) )
  1449.                 {
  1450.                     word = textFromOneNode( doc, node->content->content);
  1451.                     if ( !IsWhitespace(word) )
  1452.                         numValidTH++;
  1453.                 }
  1454.             }
  1455.         }
  1456.         
  1457.         node = node->next;
  1458.     }
  1459.  
  1460.     if (numTR == numValidTH)
  1461.     {
  1462.         doc->access.HasValidRowHeaders = yes;
  1463.     }
  1464.  
  1465.     if ( numTR >= 2 &&
  1466.          numTR > numValidTH &&
  1467.          numValidTH >= 2 &&
  1468.          doc->access.HasTH == yes )
  1469.     {
  1470.         doc->access.HasInvalidRowHeader = yes;
  1471.     }
  1472. }
  1473.  
  1474.  
  1475. /**********************************************************
  1476. * CheckColumns
  1477. *
  1478. * Check to see that each table has a column of headers if
  1479. * a row of columns doesn't exist.  
  1480. **********************************************************/
  1481.  
  1482. static void CheckColumns( TidyDocImpl* doc, Node* node )
  1483. {
  1484.     Node* tnode;
  1485.     tmbstr word;
  1486.     int numTH = 0;
  1487.     Bool isMissingHeader = no;
  1488.  
  1489.     doc->access.CheckedHeaders++;
  1490.  
  1491.     /* Table must have row of headers if headers for columns don't exist */
  1492.     if ( nodeIsTH(node->content) )
  1493.     {
  1494.         doc->access.HasTH = yes;
  1495.  
  1496.         for ( tnode = node->content; tnode; tnode = tnode->next )
  1497.         {
  1498.             if ( nodeIsTH(tnode) )
  1499.             {
  1500.                 if ( nodeIsText(tnode->content) )
  1501.                 {
  1502.                     word = textFromOneNode( doc, tnode->content);
  1503.                     if ( !IsWhitespace(word) )
  1504.                         numTH++;
  1505.                 }
  1506.             }
  1507.             else
  1508.             {
  1509.                 isMissingHeader = yes;
  1510.             }
  1511.         }
  1512.     }
  1513.  
  1514.     if ( !isMissingHeader && numTH > 0 )
  1515.         doc->access.HasValidColumnHeaders = yes;
  1516.  
  1517.     if ( isMissingHeader && numTH >= 2 )
  1518.         doc->access.HasInvalidColumnHeader = yes;
  1519. }
  1520.  
  1521.  
  1522. /*****************************************************
  1523. * CheckTH
  1524. *
  1525. * Checks to see if the header provided for a table
  1526. * requires an abbreviation. (only required if the 
  1527. * length of the header is greater than 15 characters)
  1528. *****************************************************/
  1529.  
  1530. static void CheckTH( TidyDocImpl* doc, Node* node )
  1531. {
  1532.     Bool HasAbbr = no;
  1533.     tmbstr word = NULL;
  1534.     AttVal* av;
  1535.  
  1536.     if (doc->access.PRIORITYCHK == 3)
  1537.     {
  1538.         /* Checks TH element for 'ABBR' attribute */
  1539.         for (av = node->attributes; av != NULL; av = av->next)
  1540.         {
  1541.             if ( attrIsABBR(av) )
  1542.             {
  1543.                 /* Value must not be NULL and must be less than 15 characters */
  1544.                 if ((av->value != NULL)&&
  1545.                     (IsWhitespace (av->value) == no))
  1546.                 {
  1547.                     HasAbbr = yes;
  1548.                 }
  1549.  
  1550.                 if ((av->value == NULL)||
  1551.                     (tmbstrlen (av->value) == 0))
  1552.                 {
  1553.                     HasAbbr = yes;
  1554.                     ReportAccessWarning( doc, node, TABLE_MAY_REQUIRE_HEADER_ABBR_NULL);
  1555.                 }
  1556.                 
  1557.                 if ((IsWhitespace (av->value) == yes)&&
  1558.                     (tmbstrlen (av->value) > 0))
  1559.                 {
  1560.                     HasAbbr = yes;
  1561.                     ReportAccessWarning( doc, node, TABLE_MAY_REQUIRE_HEADER_ABBR_SPACES);
  1562.                 }
  1563.             }
  1564.         }
  1565.  
  1566.         /* If the header is greater than 15 characters, an abbreviation is needed */
  1567.         word = textFromOneNode( doc, node->content);
  1568.  
  1569.         if ((word != NULL)&&
  1570.             (IsWhitespace (word) == no))
  1571.         {
  1572.             /* Must have 'ABBR' attribute if header is > 15 characters */
  1573.             if ((tmbstrlen (word) > 15)&&
  1574.                 (HasAbbr == no))
  1575.             {
  1576.                 ReportAccessWarning( doc, node, TABLE_MAY_REQUIRE_HEADER_ABBR);
  1577.             }
  1578.         }
  1579.     }
  1580. }
  1581.  
  1582.  
  1583. /*****************************************************************
  1584. * CheckMultiHeaders
  1585. *
  1586. * Layout tables should make sense when linearized.
  1587. * TABLE must contain at least one TH element.
  1588. * This technique applies only to tables used for layout purposes, 
  1589. * not to data tables. Checks for column of multiple headers.
  1590. *****************************************************************/
  1591.  
  1592. static void CheckMultiHeaders( TidyDocImpl* doc, Node* node )
  1593. {
  1594.     Node* TNode;
  1595.     Node* temp;
  1596.     
  1597.     Bool validColSpanRows = yes;
  1598.     Bool validColSpanColumns = yes;
  1599.  
  1600.     int flag = 0;
  1601.  
  1602.     if ( doc->access.PRIORITYCHK == 1 ||
  1603.          doc->access.PRIORITYCHK == 2 ||
  1604.          doc->access.PRIORITYCHK == 3 )
  1605.     {
  1606.         if (node->content != NULL)
  1607.         {
  1608.             TNode = node->content;
  1609.  
  1610.             /* 
  1611.                Checks for column of multiple headers found 
  1612.                within a data table. 
  1613.             */
  1614.             while (TNode != NULL)
  1615.             {
  1616.                 if ( nodeIsTR(TNode) )
  1617.                 {
  1618.                     if (TNode->content != NULL)
  1619.                     {
  1620.                         temp = TNode->content;
  1621.  
  1622.                         if ( nodeIsTH(temp) )
  1623.                         {
  1624.                             AttVal* av;
  1625.                             for (av = temp->attributes; av != NULL; av = av->next)
  1626.                             {
  1627.                                 if ( attrIsROWSPAN(av) )
  1628.                                 {
  1629.                                     if (atoi(av->value) > 1)
  1630.                                     {
  1631.                                         validColSpanRows = no;
  1632.                                     }
  1633.                                 }
  1634.                             }
  1635.                         }
  1636.  
  1637.                         /* The number of TH elements found within TR element */
  1638.                         if (flag == 0)
  1639.                         {
  1640.                             while (temp != NULL)
  1641.                             {
  1642.                                 /* 
  1643.                                    Must contain at least one TH element 
  1644.                                    within in the TR element 
  1645.                                 */
  1646.                                 if ( nodeIsTH(temp) )
  1647.                                 {
  1648.                                     AttVal* av;
  1649.                                     for (av = temp->attributes; av != NULL; av = av->next)
  1650.                                     {
  1651.                                         if ( attrIsCOLSPAN(av) )
  1652.                                         {
  1653.                                             if (atoi(av->value) > 1)
  1654.                                             {
  1655.                                                 validColSpanColumns = no;
  1656.                                             }
  1657.                                         }
  1658.                                     }
  1659.                                 }
  1660.  
  1661.                                 temp = temp->next;
  1662.                             }    
  1663.  
  1664.                             flag = 1;
  1665.                         }
  1666.                     }
  1667.                 }
  1668.             
  1669.                 TNode = TNode->next;
  1670.             }
  1671.  
  1672.             /* Displays HTML 4 Table Algorithm when multiple column of headers used */
  1673.             if (validColSpanRows == no)
  1674.             {
  1675.                 ReportAccessWarning( doc, node, DATA_TABLE_REQUIRE_MARKUP_ROW_HEADERS );
  1676.                 DisplayHTMLTableAlgorithm( doc );
  1677.             }
  1678.  
  1679.             if (validColSpanColumns == no)
  1680.             {
  1681.                 ReportAccessWarning( doc, node, DATA_TABLE_REQUIRE_MARKUP_COLUMN_HEADERS );
  1682.                 DisplayHTMLTableAlgorithm( doc );
  1683.             }
  1684.         }
  1685.     }
  1686. }
  1687.  
  1688.  
  1689. /****************************************************
  1690. * CheckTable
  1691. *
  1692. * Checks the TABLE element to ensure that the
  1693. * table is not missing any headers.  Must have either
  1694. * a row or column of headers.  
  1695. ****************************************************/
  1696.  
  1697. static void CheckTable( TidyDocImpl* doc, Node* node )
  1698. {
  1699.     Node* TNode;
  1700.     Node* temp;
  1701.  
  1702.     tmbstr word = NULL;
  1703.  
  1704.     int numTR = 0;
  1705.  
  1706.     Bool HasSummary = no;
  1707.     Bool HasCaption = no;
  1708.  
  1709.     if (doc->access.PRIORITYCHK == 3)
  1710.     {
  1711.         AttVal* av;
  1712.         /* Table must have a 'SUMMARY' describing the purpose of the table */
  1713.         for (av = node->attributes; av != NULL; av = av->next)
  1714.         {
  1715.             if ( attrIsSUMMARY(av) )
  1716.             {
  1717.                 if ( hasValue(av) )
  1718.                 {
  1719.                     if (!AttrContains(av, "summary") && 
  1720.                         !AttrContains(av, "table"))
  1721.                     {
  1722.                         HasSummary = yes;
  1723.                     }
  1724.  
  1725.                     if (AttrContains(av, "summary") && 
  1726.                         AttrContains(av, "table"))
  1727.                     {
  1728.                         HasSummary = yes;
  1729.                         ReportAccessError( doc, node, TABLE_SUMMARY_INVALID_PLACEHOLDER );
  1730.                     }
  1731.                 }
  1732.  
  1733.                 if ( av->value == NULL || tmbstrlen(av->value) == 0 )
  1734.                 {
  1735.                     HasSummary = yes;
  1736.                     ReportAccessError( doc, node, TABLE_SUMMARY_INVALID_NULL );
  1737.                 }
  1738.                 else if ( IsWhitespace(av->value) && tmbstrlen(av->value) > 0 )
  1739.                 {
  1740.                     HasSummary = yes;
  1741.                     ReportAccessError( doc, node, TABLE_SUMMARY_INVALID_SPACES );
  1742.                 }
  1743.             }
  1744.         }
  1745.  
  1746.         /* TABLE must have content. */
  1747.         if (node->content == NULL)
  1748.         {
  1749.             ReportAccessError( doc, node, DATA_TABLE_MISSING_HEADERS);
  1750.         
  1751.             return;
  1752.         }
  1753.     }
  1754.  
  1755.     if ((doc->access.PRIORITYCHK == 1)||
  1756.         (doc->access.PRIORITYCHK == 2)||
  1757.         (doc->access.PRIORITYCHK == 3))
  1758.     {
  1759.         /* Checks for multiple headers */
  1760.         CheckMultiHeaders( doc, node );
  1761.     }
  1762.     
  1763.     if ((doc->access.PRIORITYCHK == 2)||
  1764.         (doc->access.PRIORITYCHK == 3))
  1765.     {
  1766.         /* Table must have a CAPTION describing the purpose of the table */
  1767.         if ( nodeIsCAPTION(node->content) )
  1768.         {
  1769.             TNode = node->content;
  1770.  
  1771.             if (TNode->content->tag == NULL)
  1772.             {
  1773.                 word = getTextNodeClear( doc, TNode);
  1774.             }
  1775.  
  1776.             if ( !IsWhitespace(word) )
  1777.             {
  1778.                 HasCaption = yes;
  1779.             }
  1780.         }
  1781.  
  1782.         if (HasCaption == no)
  1783.         {
  1784.             ReportAccessError( doc, node, TABLE_MISSING_CAPTION);
  1785.         }
  1786.     }
  1787.  
  1788.     
  1789.     if (node->content != NULL)
  1790.     {
  1791.         if ( nodeIsCAPTION(node->content) && nodeIsTR(node->content->next) )
  1792.         {
  1793.             CheckColumns( doc, node->content->next );
  1794.         }
  1795.         else if ( nodeIsTR(node->content) )
  1796.         {
  1797.             CheckColumns( doc, node->content );
  1798.         }
  1799.     }
  1800.     
  1801.     if ( ! doc->access.HasValidColumnHeaders )
  1802.     {
  1803.         if (node->content != NULL)
  1804.         {
  1805.             if ( nodeIsCAPTION(node->content) && nodeIsTR(node->content->next) )
  1806.             {
  1807.                 CheckRows( doc, node->content->next);
  1808.             }
  1809.             else if ( nodeIsTR(node->content) )
  1810.             {
  1811.                 CheckRows( doc, node->content);
  1812.             }
  1813.         }
  1814.     }
  1815.     
  1816.     
  1817.     if ( doc->access.PRIORITYCHK == 3 )
  1818.     {
  1819.         /* Suppress warning for missing 'SUMMARY for HTML 2.0 and HTML 3.2 */
  1820.         if (HasSummary == no)
  1821.         {
  1822.             ReportAccessError( doc, node, TABLE_MISSING_SUMMARY);
  1823.         }
  1824.     }
  1825.  
  1826.     if ( doc->access.PRIORITYCHK == 2 ||
  1827.          doc->access.PRIORITYCHK == 3 )
  1828.     {
  1829.         if (node->content != NULL)
  1830.         {
  1831.             temp = node->content;
  1832.  
  1833.             while (temp != NULL)
  1834.             {
  1835.                 if ( nodeIsTR(temp) )
  1836.                 {
  1837.                     numTR++;
  1838.                 }
  1839.  
  1840.                 temp = temp->next;
  1841.             }
  1842.  
  1843.             if (numTR == 1)
  1844.             {
  1845.                 ReportAccessWarning( doc, node, LAYOUT_TABLES_LINEARIZE_PROPERLY);
  1846.             }
  1847.         }
  1848.     
  1849.         if ( doc->access.HasTH )
  1850.         {
  1851.             ReportAccessWarning( doc, node, LAYOUT_TABLE_INVALID_MARKUP);
  1852.         }
  1853.     }
  1854.  
  1855.     if ( doc->access.PRIORITYCHK == 1 ||
  1856.          doc->access.PRIORITYCHK == 2 ||
  1857.          doc->access.PRIORITYCHK == 3 )
  1858.     {
  1859.         if ( doc->access.CheckedHeaders == 2 )
  1860.         {
  1861.             if ( !doc->access.HasValidRowHeaders &&
  1862.                  !doc->access.HasValidColumnHeaders &&
  1863.                  !doc->access.HasInvalidRowHeader &&
  1864.                  !doc->access.HasInvalidColumnHeader  )
  1865.             {
  1866.                 ReportAccessError( doc, node, DATA_TABLE_MISSING_HEADERS);
  1867.             }
  1868.  
  1869.             if ( !doc->access.HasValidRowHeaders && 
  1870.                  doc->access.HasInvalidRowHeader )
  1871.             {
  1872.                 ReportAccessError( doc, node, DATA_TABLE_MISSING_HEADERS_ROW);
  1873.             }
  1874.  
  1875.             if ( !doc->access.HasValidColumnHeaders &&
  1876.                  doc->access.HasInvalidColumnHeader )
  1877.             {
  1878.                 ReportAccessError( doc, node, DATA_TABLE_MISSING_HEADERS_COLUMN);
  1879.             }
  1880.         }
  1881.     }
  1882. }
  1883.  
  1884.  
  1885. /***************************************************
  1886. * CheckASCII
  1887. * Checks for valid text equivalents for XMP and PRE
  1888. * elements for ASCII art.  Ensures that there is
  1889. * a skip over link to skip multi-lined ASCII art.
  1890. ***************************************************/
  1891.  
  1892. static void CheckASCII( TidyDocImpl* doc, Node* node )
  1893. {
  1894.     Node* temp1;
  1895.     Node* temp2;
  1896.  
  1897.     tmbstr skipOver = NULL;
  1898.     Bool IsAscii = no;
  1899.     int HasSkipOverLink = 0;
  1900.         
  1901.     uint i, x;
  1902.     int newLines = -1;
  1903.     tmbchar compareLetter;
  1904.     int matchingCount = 0;
  1905.     AttVal* av;
  1906.     
  1907.     if ( doc->access.PRIORITYCHK == 1 ||
  1908.          doc->access.PRIORITYCHK == 2 ||
  1909.          doc->access.PRIORITYCHK == 3 )
  1910.     {
  1911.         /* 
  1912.            Checks the text within the PRE and XMP tags to see if ascii 
  1913.            art is present 
  1914.         */
  1915.         for (i = node->content->start + 1; i < node->content->end; i++)
  1916.         {
  1917.             matchingCount = 0;
  1918.  
  1919.             /* Counts the number of lines of text */
  1920.             if (doc->lexer->lexbuf[i] == '\n')
  1921.             {
  1922.                 newLines++;
  1923.             }
  1924.             
  1925.             compareLetter = doc->lexer->lexbuf[i];
  1926.  
  1927.             /* Counts consecutive character matches */
  1928.             for (x = i; x < i + 5; x++)
  1929.             {
  1930.                 if (doc->lexer->lexbuf[x] == compareLetter)
  1931.                 {
  1932.                     matchingCount++;
  1933.                 }
  1934.  
  1935.                 else
  1936.                 {
  1937.                     break;
  1938.                 }
  1939.             }
  1940.  
  1941.             /* Must have at least 5 consecutive character matches */
  1942.             if (matchingCount >= 5)
  1943.             {
  1944.                 break;
  1945.             }
  1946.         }
  1947.  
  1948.         /* 
  1949.            Must have more than 6 lines of text OR 5 or more consecutive 
  1950.            letters that are the same for there to be ascii art 
  1951.         */
  1952.         if (newLines >= 6 || matchingCount >= 5)
  1953.         {
  1954.             IsAscii = yes;
  1955.         }
  1956.  
  1957.         /* Checks for skip over link if ASCII art is present */
  1958.         if (IsAscii == yes)
  1959.         {
  1960.             if (node->prev != NULL && node->prev->prev != NULL)
  1961.             {
  1962.                 temp1 = node->prev->prev;
  1963.  
  1964.                 /* Checks for 'HREF' attribute */
  1965.                 for (av = temp1->attributes; av != NULL; av = av->next)
  1966.                 {
  1967.                     if ( attrIsHREF(av) && hasValue(av) )
  1968.                     {
  1969.                         skipOver = av->value;
  1970.                         HasSkipOverLink++;
  1971.                     }
  1972.                 }
  1973.             }
  1974.         }
  1975.     }
  1976.  
  1977.     if ((doc->access.PRIORITYCHK == 2)||
  1978.         (doc->access.PRIORITYCHK == 3))
  1979.     {
  1980.         /* 
  1981.            Checks for A element following PRE to ensure proper skipover link
  1982.            only if there is an A element preceding PRE.
  1983.         */
  1984.         if (HasSkipOverLink == 1)
  1985.         {
  1986.             if ( nodeIsA(node->next) )
  1987.             {
  1988.                 temp2 = node->next;
  1989.                 
  1990.                 /* Checks for 'NAME' attribute */
  1991.                 for (av = temp2->attributes; av != NULL; av = av->next)
  1992.                 {
  1993.                     if ( attrIsNAME(av) && hasValue(av) )
  1994.                     {
  1995.                         /* 
  1996.                            Value within the 'HREF' attribute must be the same
  1997.                            as the value within the 'NAME' attribute for valid
  1998.                            skipover.
  1999.                         */
  2000.                         if ( strstr(skipOver, av->value) != NULL )
  2001.                         {
  2002.                             HasSkipOverLink++;
  2003.                         }
  2004.                     }
  2005.                 }
  2006.             }
  2007.         }
  2008.  
  2009.         if (IsAscii == yes)
  2010.         {
  2011.             ReportAccessError( doc, node, ASCII_REQUIRES_DESCRIPTION);
  2012.         }
  2013.  
  2014.         if (HasSkipOverLink < 2)
  2015.         {
  2016.             if (IsAscii == yes)
  2017.             {
  2018.                 ReportAccessError( doc, node, SKIPOVER_ASCII_ART);
  2019.             }
  2020.         }
  2021.     }
  2022. }
  2023.  
  2024.  
  2025. /***********************************************************
  2026. * CheckFormControls
  2027. *
  2028. * <form> must have valid 'FOR' attribute, and <label> must
  2029. * have valid 'ID' attribute for valid form control.
  2030. ***********************************************************/
  2031.  
  2032. static void CheckFormControls( TidyDocImpl* doc, Node* node )
  2033. {
  2034.     if ( !doc->access.HasValidFor &&
  2035.          doc->access.HasValidId )
  2036.     {
  2037.         ReportAccessError( doc, node, ASSOCIATE_LABELS_EXPLICITLY_FOR);
  2038.     }    
  2039.  
  2040.     if ( !doc->access.HasValidId &&
  2041.          doc->access.HasValidFor )
  2042.     {
  2043.         ReportAccessError( doc, node, ASSOCIATE_LABELS_EXPLICITLY_ID);
  2044.     }
  2045.  
  2046.     if ( !doc->access.HasValidId &&
  2047.          !doc->access.HasValidFor )
  2048.     {
  2049.         ReportAccessError( doc, node, ASSOCIATE_LABELS_EXPLICITLY);
  2050.     }
  2051. }
  2052.  
  2053.  
  2054. /************************************************************
  2055. * CheckLabel
  2056. *
  2057. * Check for valid 'FOR' attribute within the LABEL element
  2058. ************************************************************/
  2059.  
  2060. static void CheckLabel( TidyDocImpl* doc, Node* node )
  2061. {
  2062.     if ( doc->access.PRIORITYCHK == 2 ||
  2063.          doc->access.PRIORITYCHK == 3 )
  2064.     {    
  2065.         /* Checks for valid 'FOR' attribute */
  2066.         AttVal* av = attrGetFOR( node );
  2067.         if ( hasValue(av) )
  2068.             doc->access.HasValidFor = yes;
  2069.  
  2070.         if ( ++doc->access.ForID == 2 )
  2071.         {
  2072.             doc->access.ForID = 0;
  2073.             CheckFormControls( doc, node );
  2074.         }
  2075.     }
  2076. }
  2077.  
  2078.  
  2079. /************************************************************
  2080. * CheckInputLabel
  2081. * Checks for valid 'ID' attribute within the INPUT element.
  2082. * Checks to see if there is a LABEL directly before
  2083. * or after the INPUT element determined by the 'TYPE'.  
  2084. * Each INPUT element must have a LABEL describing the form.
  2085. ************************************************************/
  2086.  
  2087. static void CheckInputLabel( TidyDocImpl* doc, Node* node )
  2088. {
  2089.     int flag = 0;
  2090.  
  2091.     tmbstr word = NULL;
  2092.     tmbstr text = NULL;
  2093.  
  2094.     AttVal* av;
  2095.  
  2096.     Bool HasLabelBefore = no;
  2097.     Bool HasLabelAfter = no;
  2098.     Bool HasValidLabel = no;
  2099.  
  2100.     if ( doc->access.PRIORITYCHK == 2 ||
  2101.          doc->access.PRIORITYCHK == 3 )
  2102.     {
  2103.  
  2104.         /* Checks attributes within the INPUT element */
  2105.         for (av = node->attributes; av != NULL; av = av->next)
  2106.         {
  2107.             /* Must have valid 'ID' value */
  2108.             if ( attrIsID(av) && hasValue(av) )
  2109.                 doc->access.HasValidId = yes;
  2110.     
  2111.             /* 
  2112.                Determines where the LABEL should be located determined by 
  2113.                the 'TYPE' of form the INPUT is.
  2114.             */
  2115.             else if ( attrIsTYPE(av) && hasValue(av) )
  2116.             {
  2117.                 if (AttrValueIs(av, "checkbox") ||
  2118.                     AttrValueIs(av, "radio")    ||
  2119.                     AttrValueIs(av, "text")     ||
  2120.                     AttrValueIs(av, "password") ||
  2121.                     AttrValueIs(av, "file"))
  2122.                 {
  2123.                     if ( node->prev != NULL &&
  2124.                          node->prev->prev != NULL )
  2125.                     {
  2126.                         Node* temp = node->prev->prev;
  2127.                         if ( nodeIsLABEL(temp) )
  2128.                         {
  2129.                             flag = 1;
  2130.                             if ( nodeIsText(temp->content) )
  2131.                             {
  2132.                                 word = textFromOneNode( doc, temp->content );
  2133.                                 if ( !IsWhitespace(word) )
  2134.                                     HasLabelBefore = yes;
  2135.                             }
  2136.                         }
  2137.                         
  2138.                         if ( HasLabelBefore && nodeIsText(node->prev) )
  2139.                         {
  2140.                             text = textFromOneNode( doc, node->prev );
  2141.                             if ( IsWhitespace(text) )
  2142.                                 HasValidLabel = yes;
  2143.                         }
  2144.                     }
  2145.  
  2146.                     if ( flag == 0 )
  2147.                     {
  2148.                         if ( node->next != NULL &&
  2149.                              node->next->next != NULL )
  2150.                         {
  2151.                             Node* temp = node->next->next;
  2152.                             if ( nodeIsLABEL(temp) &&
  2153.                                  nodeIsText(temp->content) )
  2154.                             {
  2155.                                 word = textFromOneNode( doc, temp->content);
  2156.                                 if ( !IsWhitespace(word) )
  2157.                                     HasLabelAfter = yes;
  2158.                             }
  2159.  
  2160.                             if ( HasLabelAfter && nodeIsText(node->next) )
  2161.                             {
  2162.                                 text = textFromOneNode( doc, node->next);
  2163.                                 if ( IsWhitespace(text) )
  2164.                                     HasValidLabel = yes;
  2165.                             }
  2166.                         }
  2167.                     }
  2168.                 }
  2169.  
  2170.                 /* The following 'TYPES' do not require a LABEL */
  2171.                 if (AttrValueIs(av, "image")  ||
  2172.                     AttrValueIs(av, "submit") ||
  2173.                     AttrValueIs(av, "reset")  ||
  2174.                     AttrValueIs(av, "button"))
  2175.                 {
  2176.                     HasValidLabel = yes;
  2177.                 }
  2178.             }
  2179.         }
  2180.  
  2181.         if ( !HasValidLabel )
  2182.         {
  2183.             if ( HasLabelBefore )
  2184.               ReportAccessError( doc, node, LABEL_NEEDS_REPOSITIONING_BEFORE_INPUT );
  2185.        
  2186.             if ( HasLabelAfter )
  2187.               ReportAccessError( doc, node, LABEL_NEEDS_REPOSITIONING_AFTER_INPUT );
  2188.         }
  2189.         
  2190.         if ( ++doc->access.ForID == 2 )
  2191.         {
  2192.             doc->access.ForID = 0;
  2193.             CheckFormControls( doc, node );
  2194.         }
  2195.     }
  2196. }
  2197.  
  2198.  
  2199. /***************************************************************
  2200. * CheckInputAttributes 
  2201. *
  2202. * INPUT element must have a valid 'ALT' attribute if the
  2203. * 'VALUE' attribute is present.
  2204. ***************************************************************/
  2205.  
  2206. static void CheckInputAttributes( TidyDocImpl* doc, Node* node )
  2207. {
  2208.     Bool HasValue = no;
  2209.     Bool HasAlt = no;
  2210.     Bool MustHaveAlt = no;
  2211.     Bool MustHaveValue = no;
  2212.     AttVal* av;
  2213.  
  2214.     /* Checks attributes within the INPUT element */
  2215.     for (av = node->attributes; av != NULL; av = av->next)
  2216.     {
  2217.         /* 'VALUE' must be found if the 'TYPE' is 'text' or 'checkbox' */
  2218.         if ( attrIsTYPE(av) && hasValue(av) )
  2219.         {
  2220.             if ( doc->access.PRIORITYCHK == 1 ||
  2221.                  doc->access.PRIORITYCHK == 2 ||
  2222.                  doc->access.PRIORITYCHK == 3 )
  2223.             {
  2224.                 if (AttrValueIs(av, "image"))
  2225.                 {
  2226.                     MustHaveAlt = yes;
  2227.                 }
  2228.             }
  2229.  
  2230.             if ( doc->access.PRIORITYCHK == 3 )
  2231.             {
  2232.                 if (AttrValueIs(av, "text") ||
  2233.                     AttrValueIs(av, "checkbox"))
  2234.                 {    
  2235.                     MustHaveValue = yes;
  2236.                 }
  2237.             }
  2238.         }
  2239.         
  2240.         if ( attrIsALT(av) && hasValue(av) )
  2241.         {
  2242.             HasAlt = yes;
  2243.         }
  2244.  
  2245.         if ( attrIsVALUE(av) )
  2246.         {
  2247.             if ( hasValue(av) )
  2248.             {
  2249.                 HasValue = yes;
  2250.             }
  2251.             else if ( av->value == NULL || tmbstrlen(av->value) == 0 )
  2252.             {
  2253.                 HasValue = yes;
  2254.                 ReportAccessError( doc, node, FORM_CONTROL_DEFAULT_TEXT_INVALID_NULL );
  2255.             }
  2256.             else if ( IsWhitespace(av->value) && tmbstrlen(av->value) > 0 )
  2257.             {
  2258.                 HasValue = yes;
  2259.                 ReportAccessError( doc, node, FORM_CONTROL_DEFAULT_TEXT_INVALID_SPACES );
  2260.             }
  2261.         }
  2262.     }
  2263.  
  2264.     if ( MustHaveAlt && !HasAlt )
  2265.     {
  2266.         ReportAccessError( doc, node, IMG_BUTTON_MISSING_ALT );
  2267.     }
  2268.  
  2269.     if ( MustHaveValue && !HasValue )
  2270.     {
  2271.         ReportAccessError( doc, node, FORM_CONTROL_REQUIRES_DEFAULT_TEXT );
  2272.     }
  2273. }
  2274.  
  2275.  
  2276. /***************************************************************
  2277. * CheckFrameSet
  2278. *
  2279. * Frameset must have valid NOFRAME section.  Must contain some 
  2280. * text but must not contain information telling user to update 
  2281. * browsers, 
  2282. ***************************************************************/
  2283.  
  2284. static void CheckFrameSet( TidyDocImpl* doc, Node* node )
  2285. {
  2286.     Node* temp;
  2287.     tmbstr word;
  2288.     
  2289.     Bool HasNoFrames = no;
  2290.  
  2291.     if ((doc->access.PRIORITYCHK == 1)||
  2292.         (doc->access.PRIORITYCHK == 2)||
  2293.         (doc->access.PRIORITYCHK == 3))
  2294.     {
  2295.         if (node->content != NULL)
  2296.         {
  2297.             temp = node->content;
  2298.  
  2299.             while (temp != NULL)
  2300.             {
  2301.                 if ( nodeIsA(temp) )
  2302.                 {
  2303.                     ReportAccessError( doc, temp, NOFRAMES_INVALID_LINK);
  2304.                 }
  2305.                 else if ( nodeIsNOFRAMES(temp) )
  2306.                 {
  2307.                     HasNoFrames = yes;
  2308.  
  2309.                     if ( temp->content && nodeIsP(temp->content->content) )
  2310.                     {
  2311.                         Node* para = temp->content->content;
  2312.                         if ( nodeIsText(para->content) )
  2313.                         {
  2314.                             word = textFromOneNode( doc, para->content );
  2315.                             if ( word && strstr(word, "browser") != NULL )
  2316.                             {
  2317.                                 ReportAccessError( doc, para, NOFRAMES_INVALID_CONTENT );
  2318.                             }
  2319.                         }
  2320.                     }
  2321.                     else if (temp->content == NULL)
  2322.                     {
  2323.                         ReportAccessError( doc, temp, NOFRAMES_INVALID_NO_VALUE);
  2324.                     }
  2325.                     else if ( temp->content &&
  2326.                               IsWhitespace(textFromOneNode(doc, temp->content)) )
  2327.                     {
  2328.                         ReportAccessError( doc, temp, NOFRAMES_INVALID_NO_VALUE);
  2329.                     }
  2330.                 }
  2331.  
  2332.                 temp = temp->next;
  2333.             }
  2334.         }
  2335.  
  2336.         if (HasNoFrames == no)
  2337.         {
  2338.             ReportAccessError( doc, node, FRAME_MISSING_NOFRAMES);
  2339.         }
  2340.     }
  2341. }
  2342.  
  2343.  
  2344. /***********************************************************
  2345. * CheckHeaderNesting
  2346. *
  2347. * Checks for heading increases and decreases.  Headings must
  2348. * not increase by more than one header level, but may
  2349. * decrease at from any level to any level.  Text within 
  2350. * headers must not be more than 20 words in length.  
  2351. ***********************************************************/
  2352.  
  2353. static void CheckHeaderNesting( TidyDocImpl* doc, Node* node )
  2354. {
  2355.     Node* temp;
  2356.     tmbstr word;
  2357.     uint i;
  2358.     int numWords = 1;
  2359.  
  2360.     Bool IsValidIncrease = no;
  2361.     Bool NeedsDescription = no;
  2362.  
  2363.     if ( doc->access.PRIORITYCHK == 2 ||
  2364.          doc->access.PRIORITYCHK == 3 )
  2365.     {
  2366.         /* 
  2367.            Text within header element cannot contain more than 20 words without
  2368.            a separate description
  2369.         */
  2370.         if (node->content->tag == NULL)
  2371.         {
  2372.             word = textFromOneNode( doc, node->content);
  2373.  
  2374.             for(i = 0; i < tmbstrlen (word); i++)
  2375.             {
  2376.                 if (word[i] == ' ')
  2377.                 {
  2378.                     numWords++;
  2379.                 }
  2380.             }
  2381.  
  2382.             if (numWords > 20)
  2383.             {
  2384.                 NeedsDescription = yes;
  2385.             }
  2386.         }
  2387.  
  2388.         /* Header following must be same level or same plus 1 for
  2389.         ** valid heading increase size.  E.g. H1 -> H1, H2.  H3 -> H3, H4
  2390.         */
  2391.         if ( nodeIsHeader(node) )
  2392.         {
  2393.             uint level = nodeHeaderLevel( node );
  2394.             IsValidIncrease = yes;
  2395.  
  2396.             for ( temp = node->next; temp != NULL; temp = temp->next )
  2397.             {
  2398.                 uint nested = nodeHeaderLevel( temp );
  2399.                 if ( nested >= level )
  2400.                 {
  2401.                     IsValidIncrease = ( nested <= level + 1 );
  2402.                     break;
  2403.                 }
  2404.             }
  2405.         }
  2406.  
  2407.         if ( !IsValidIncrease )
  2408.             ReportAccessWarning( doc, node, HEADERS_IMPROPERLY_NESTED );
  2409.     
  2410.         if ( NeedsDescription )
  2411.             ReportAccessWarning( doc, node, HEADER_USED_FORMAT_TEXT );    
  2412.     }
  2413. }
  2414.  
  2415.  
  2416. /*************************************************************
  2417. * CheckParagraphHeader
  2418. *
  2419. * Checks to ensure that P elements are not headings.  Must be
  2420. * greater than 10 words in length, and they must not be in bold,
  2421. * or italics, or underlined, etc.
  2422. *************************************************************/
  2423.  
  2424. static void CheckParagraphHeader( TidyDocImpl* doc, Node* node )
  2425. {
  2426.     Bool IsNotHeader = no;
  2427.     Node* temp;
  2428.  
  2429.     if ((doc->access.PRIORITYCHK == 2)||
  2430.         (doc->access.PRIORITYCHK == 3))
  2431.     {
  2432.         /* Cannot contain text formatting elements */
  2433.         if (node->content != NULL)   
  2434.         {                     
  2435.             if (node->content->tag != NULL)
  2436.             {
  2437.                 temp = node->content;
  2438.  
  2439.                 while (temp != NULL)
  2440.                 {
  2441.                     if (temp->tag == NULL)
  2442.                     {
  2443.                         IsNotHeader = yes;
  2444.                         break;
  2445.                     }
  2446.                         
  2447.                     temp = temp->next;
  2448.                 }
  2449.             }
  2450.  
  2451.             if ( !IsNotHeader )
  2452.             {
  2453.                 if ( nodeIsSTRONG(node->content) )
  2454.                 {
  2455.                     ReportAccessWarning( doc, node, POTENTIAL_HEADER_BOLD);
  2456.                 }
  2457.  
  2458.                 if ( nodeIsU(node->content) )
  2459.                 {
  2460.                     ReportAccessWarning( doc, node, POTENTIAL_HEADER_UNDERLINE);
  2461.                 }
  2462.  
  2463.                 if ( nodeIsEM(node->content) )
  2464.                 {
  2465.                     ReportAccessWarning( doc, node, POTENTIAL_HEADER_ITALICS);
  2466.                 }
  2467.             }
  2468.         }
  2469.     }
  2470. }
  2471.  
  2472.  
  2473. /*********************************************************
  2474. * CheckSelect
  2475. *
  2476. * Checks to see if a LABEL follows the SELECT element.
  2477. *********************************************************/
  2478.  
  2479. static void CheckSelect( TidyDocImpl* doc, Node* node )
  2480. {
  2481.     Node* temp;
  2482.     tmbstr label;
  2483.     int flag = 0;
  2484.  
  2485.     Bool HasLabelBefore = no;
  2486.     Bool HasLabelAfter = no;
  2487.  
  2488.     if ( doc->access.PRIORITYCHK == 2 ||
  2489.          doc->access.PRIORITYCHK == 3 )
  2490.     {
  2491.         /* Check to see if there is a LABEL preceding SELECT */
  2492.         if ( node->prev != NULL && node->prev->prev != NULL )
  2493.         {
  2494.             if ( nodeIsLABEL(node->prev->prev) )
  2495.             {
  2496.                 temp = node->prev->prev;
  2497.                 
  2498.                 if ( nodeIsText(temp->content) )
  2499.                 {
  2500.                     label = textFromOneNode( doc, temp->content );
  2501.                     if ( !IsWhitespace(label) )
  2502.                     {
  2503.                         flag = 1;
  2504.                         HasLabelBefore = yes;
  2505.                     }
  2506.                 }
  2507.             }
  2508.  
  2509.             /* Check to see if there is a LABEL following SELECT */
  2510.             if (flag == 0)
  2511.             {
  2512.                 if ( nodeIsLABEL(node) )
  2513.                 {
  2514.                     temp = node->next->next;
  2515.                     
  2516.                     if ( nodeIsText(temp->content) )
  2517.                     {
  2518.                         label = textFromOneNode( doc, temp->content );
  2519.                         if ( !IsWhitespace(label) )
  2520.                         {
  2521.                             flag = 1;
  2522.                             HasLabelAfter = yes;
  2523.                         }
  2524.                     }
  2525.                 }
  2526.             }
  2527.  
  2528.             if ( !HasLabelAfter )
  2529.                 ReportAccessError( doc, node, LABEL_NEEDS_REPOSITIONING_AFTER_INPUT);
  2530.  
  2531.             if (HasLabelBefore == no)
  2532.                 ReportAccessError( doc, node, LABEL_NEEDS_REPOSITIONING_BEFORE_INPUT);
  2533.         }
  2534.     }
  2535. }
  2536.  
  2537.  
  2538. /************************************************************
  2539. * CheckTextArea
  2540. *
  2541. * TEXTAREA must contain a label description either before 
  2542. * or after the TEXTAREA element. Text must exist within 
  2543. * TEXTAREA.
  2544. ************************************************************/
  2545.  
  2546. static void CheckTextArea( TidyDocImpl* doc, Node* node )
  2547. {
  2548.     int flag = 0;
  2549.     
  2550.     Bool HasLabelAfter = no;
  2551.     Bool HasLabelBefore = no;
  2552.  
  2553.     tmbstr label;
  2554.     Node* temp;
  2555.  
  2556.     if ((doc->access.PRIORITYCHK == 2)||
  2557.         (doc->access.PRIORITYCHK == 3))
  2558.     {
  2559.         /* Check to see if there is a LABEL before or after TEXTAREA */
  2560.         if (node->prev != NULL && node->prev->prev != NULL)
  2561.         {
  2562.             if ( nodeIsLABEL(node->prev->prev) )
  2563.             {
  2564.                 temp = node->prev->prev;
  2565.                 
  2566.                 if ( nodeIsText(temp->content) )
  2567.                 {
  2568.                     label = textFromOneNode( doc, temp->content );
  2569.                     if ( !IsWhitespace(label) )
  2570.                     {
  2571.                         flag = 1;
  2572.                         HasLabelBefore = yes;
  2573.                     }
  2574.                 }
  2575.             }
  2576.  
  2577.             if (flag == 0)
  2578.             {
  2579.                 if ( nodeIsLABEL(node->next->next) )
  2580.                 {
  2581.                     temp = node->next->next;
  2582.                     
  2583.                     if ( nodeIsText(temp->content) )
  2584.                     {
  2585.                         label = textFromOneNode( doc, temp->content);
  2586.                         if ( !IsWhitespace(label) )
  2587.                         {
  2588.                             flag = 1;
  2589.                             HasLabelAfter = yes;
  2590.                         }
  2591.                     }
  2592.                 }
  2593.             }
  2594.         
  2595.             if ( !HasLabelAfter )
  2596.                 ReportAccessError( doc, node, LABEL_NEEDS_REPOSITIONING_AFTER_INPUT);
  2597.  
  2598.             if ( !HasLabelBefore )
  2599.                 ReportAccessError( doc, node, LABEL_NEEDS_REPOSITIONING_BEFORE_INPUT);
  2600.         }
  2601.     }
  2602. }
  2603.  
  2604.  
  2605. /****************************************************************
  2606. * CheckEmbed
  2607. *
  2608. * Checks to see if 'SRC' is a multimedia type.  Must have 
  2609. * syncronized captions if used.
  2610. ****************************************************************/
  2611.  
  2612. static void CheckEmbed( TidyDocImpl* doc, Node* node )
  2613. {
  2614.     if ( doc->access.PRIORITYCHK == 1 ||
  2615.          doc->access.PRIORITYCHK == 2 ||
  2616.          doc->access.PRIORITYCHK == 3 )
  2617.     {
  2618.         AttVal* av = attrGetSRC( node );
  2619.         if ( hasValue(av) && IsValidMediaExtension(av->value) )
  2620.         {
  2621.              ReportAccessError( doc, node, MULTIMEDIA_REQUIRES_TEXT );
  2622.         }
  2623.     }
  2624. }
  2625.  
  2626.  
  2627. /*********************************************************************
  2628. * CheckHTMLAccess
  2629. *
  2630. * Checks HTML element for valid 'LANG' attribute.  Must be a valid
  2631. * language.  ie. 'fr' or 'en'
  2632. ********************************************************************/
  2633.  
  2634. static void CheckHTMLAccess( TidyDocImpl* doc, Node* node )
  2635. {
  2636.     Bool ValidLang = no;
  2637.  
  2638.     if ( doc->access.PRIORITYCHK == 3 )
  2639.     {
  2640.         AttVal* av = attrGetLANG( node );
  2641.         if ( av )
  2642.         {
  2643.             ValidLang = yes;
  2644.             if ( !hasValue(av) )
  2645.                 ReportAccessError( doc, node, LANGUAGE_INVALID );
  2646.         }
  2647.         if ( !ValidLang )
  2648.             ReportAccessError( doc, node, LANGUAGE_NOT_IDENTIFIED );
  2649.     }
  2650. }
  2651.  
  2652.  
  2653. /********************************************************
  2654. * CheckBlink
  2655. *
  2656. * Document must not contain the BLINK element.  
  2657. * It is invalid HTML/XHTML.
  2658. *********************************************************/
  2659.  
  2660. static void CheckBlink( TidyDocImpl* doc, Node* node )
  2661. {
  2662.     
  2663.     if ( doc->access.PRIORITYCHK == 2 ||
  2664.          doc->access.PRIORITYCHK == 3 )
  2665.     {
  2666.         /* Checks to see if text is found within the BLINK element. */
  2667.         if ( nodeIsText(node->content) )
  2668.         {
  2669.             tmbstr word = textFromOneNode( doc, node->content );
  2670.             if ( !IsWhitespace(word) )
  2671.             {
  2672.                 ReportAccessError( doc, node, REMOVE_BLINK_MARQUEE );
  2673.             }
  2674.         }
  2675.     }
  2676. }
  2677.  
  2678.  
  2679. /********************************************************
  2680. * CheckMarquee
  2681. *
  2682. * Document must not contain the MARQUEE element.
  2683. * It is invalid HTML/XHTML.
  2684. ********************************************************/
  2685.  
  2686.  
  2687. static void CheckMarquee( TidyDocImpl* doc, Node* node )
  2688. {
  2689.     if ( doc->access.PRIORITYCHK == 2 ||
  2690.          doc->access.PRIORITYCHK == 3 )
  2691.     {
  2692.         /* Checks to see if there is text in between the MARQUEE element */
  2693.         if ( nodeIsText(node) )
  2694.         {
  2695.             tmbstr word = textFromOneNode( doc, node->content);
  2696.             if ( !IsWhitespace(word) )
  2697.             {
  2698.                 ReportAccessError( doc, node, REMOVE_BLINK_MARQUEE );
  2699.             }
  2700.         }
  2701.     }
  2702. }
  2703.  
  2704.  
  2705. /**********************************************************
  2706. * CheckLink
  2707. *
  2708. * 'REL' attribute within the LINK element must not contain
  2709. * 'stylesheet'.  HTML/XHTML document is unreadable when
  2710. * style sheets are applied.  -- CPR huh?
  2711. **********************************************************/
  2712.  
  2713. static void CheckLink( TidyDocImpl* doc, Node* node )
  2714. {
  2715.     Bool HasRel = no;
  2716.     Bool HasType = no;
  2717.  
  2718.     if ( doc->access.PRIORITYCHK == 1 ||
  2719.          doc->access.PRIORITYCHK == 2 ||
  2720.          doc->access.PRIORITYCHK == 3 )
  2721.     {
  2722.         AttVal* av;
  2723.         /* Check for valid 'REL' and 'TYPE' attribute */
  2724.         for (av = node->attributes; av != NULL; av = av->next)
  2725.         {
  2726.             if ( attrIsREL(av) && hasValue(av) )
  2727.             {
  2728.                 if (AttrContains(av, "stylesheet"))
  2729.                     HasRel = yes;
  2730.             }
  2731.  
  2732.             if ( attrIsTYPE(av) && hasValue(av) )
  2733.             {
  2734.                 HasType = yes;
  2735.             }
  2736.         }
  2737.  
  2738.         ReportAccessWarning( doc, node, STYLESHEETS_REQUIRE_TESTING_LINK );
  2739.     }
  2740. }
  2741.  
  2742.  
  2743. /*******************************************************
  2744. * CheckStyle
  2745. *
  2746. * Document must not contain STYLE element.  HTML/XHTML 
  2747. * document is unreadable when style sheets are applied.
  2748. *******************************************************/
  2749.  
  2750. static void CheckStyle( TidyDocImpl* doc, Node* node )
  2751. {
  2752.     if ( doc->access.PRIORITYCHK == 1 ||
  2753.          doc->access.PRIORITYCHK == 2 ||
  2754.          doc->access.PRIORITYCHK == 3 )
  2755.     {
  2756.         ReportAccessWarning( doc, node, STYLESHEETS_REQUIRE_TESTING_STYLE_ELEMENT );
  2757.     }
  2758. }
  2759.  
  2760.  
  2761. /*************************************************************
  2762. * DynamicContent
  2763. *
  2764. * Verify that equivalents of dynamic content are updated and 
  2765. * available as often as the dynamic content.
  2766. *************************************************************/
  2767.  
  2768.  
  2769. static void DynamicContent( TidyDocImpl* doc, Node* node )
  2770. {
  2771.     if ( doc->access.PRIORITYCHK == 1 ||
  2772.          doc->access.PRIORITYCHK == 2 ||
  2773.          doc->access.PRIORITYCHK == 3 )
  2774.     {
  2775.         uint msgcode = 0;
  2776.         if ( nodeIsAPPLET(node) )
  2777.             msgcode = TEXT_EQUIVALENTS_REQUIRE_UPDATING_APPLET;
  2778.         else if ( nodeIsSCRIPT(node) )
  2779.             msgcode = TEXT_EQUIVALENTS_REQUIRE_UPDATING_SCRIPT;
  2780.         else if ( nodeIsOBJECT(node) )
  2781.             msgcode = TEXT_EQUIVALENTS_REQUIRE_UPDATING_OBJECT;
  2782.  
  2783.         if ( msgcode )
  2784.             ReportAccessWarning( doc, node, msgcode );
  2785.     }
  2786. }
  2787.  
  2788.  
  2789. /*************************************************************
  2790. * ProgrammaticObjects
  2791. *
  2792. * Verify that the page is usable when programmatic objects 
  2793. * are disabled.
  2794. *************************************************************/
  2795.  
  2796. static void ProgrammaticObjects( TidyDocImpl* doc, Node* node )
  2797. {
  2798.     if ( doc->access.PRIORITYCHK == 1 ||
  2799.          doc->access.PRIORITYCHK == 2 ||
  2800.          doc->access.PRIORITYCHK == 3 )
  2801.     {
  2802.         int msgcode = 0;
  2803.         if ( nodeIsSCRIPT(node) )
  2804.             msgcode = PROGRAMMATIC_OBJECTS_REQUIRE_TESTING_SCRIPT;
  2805.         else if ( nodeIsOBJECT(node) )
  2806.             msgcode = PROGRAMMATIC_OBJECTS_REQUIRE_TESTING_OBJECT;
  2807.         else if ( nodeIsEMBED(node) )
  2808.             msgcode = PROGRAMMATIC_OBJECTS_REQUIRE_TESTING_EMBED;
  2809.         else if ( nodeIsAPPLET(node) )
  2810.             msgcode = PROGRAMMATIC_OBJECTS_REQUIRE_TESTING_APPLET;
  2811.  
  2812.         if ( msgcode )
  2813.             ReportAccessWarning( doc, node, msgcode );
  2814.     }
  2815. }
  2816.  
  2817.  
  2818. /*************************************************************
  2819. * AccessibleCompatible
  2820. *
  2821. * Verify that programmatic objects are directly accessible.
  2822. *************************************************************/
  2823.  
  2824. static void AccessibleCompatible( TidyDocImpl* doc, Node* node )
  2825. {
  2826.     if ( doc->access.PRIORITYCHK == 1 ||
  2827.          doc->access.PRIORITYCHK == 2 ||
  2828.          doc->access.PRIORITYCHK == 3 )
  2829.     {
  2830.         int msgcode = 0;
  2831.         if ( nodeIsSCRIPT(node) )
  2832.             msgcode = ENSURE_PROGRAMMATIC_OBJECTS_ACCESSIBLE_SCRIPT;
  2833.         else if ( nodeIsOBJECT(node) )
  2834.             msgcode = ENSURE_PROGRAMMATIC_OBJECTS_ACCESSIBLE_OBJECT;
  2835.         else if ( nodeIsEMBED(node) )
  2836.             msgcode = ENSURE_PROGRAMMATIC_OBJECTS_ACCESSIBLE_EMBED;
  2837.         else if ( nodeIsAPPLET(node) )
  2838.             msgcode = ENSURE_PROGRAMMATIC_OBJECTS_ACCESSIBLE_APPLET;
  2839.  
  2840.         if ( msgcode )
  2841.             ReportAccessWarning( doc, node, msgcode );
  2842.     }
  2843. }
  2844.  
  2845.  
  2846. /********************************************************
  2847. * WordCount
  2848. *
  2849. * Counts the number of words in the document.  Must have
  2850. * more than 3 words to verify changes in natural
  2851. * language of document.
  2852. *
  2853. * CPR - Not sure what intent is here, but this 
  2854. * routine has nothing to do with changes in language.
  2855. * It seems like a bad idea to emit this message for
  2856. * every document with _more_ than 3 words!
  2857. ********************************************************/
  2858.  
  2859. static int WordCount( TidyDocImpl* doc, Node* node )
  2860. {
  2861.     int wc = 0;
  2862.  
  2863.     if ( doc->access.PRIORITYCHK == 1 ||
  2864.          doc->access.PRIORITYCHK == 2 ||
  2865.          doc->access.PRIORITYCHK == 3 )
  2866.     {
  2867.         /* Count the number of words found within a text node */
  2868.         if ( nodeIsText( node ) )
  2869.         {
  2870.             tmbchar ch;
  2871.             tmbstr word = textFromOneNode( doc, node );
  2872.             if ( !IsWhitespace(word) )
  2873.             {
  2874.                 ++wc;
  2875.                 while ( (ch = *word++) && wc < 5 )
  2876.                 {
  2877.                     if ( ch == ' ')
  2878.                         ++wc;
  2879.                 }
  2880.             }
  2881.         }
  2882.  
  2883.         for ( node = node->content; wc < 5 && node; node = node->next )
  2884.         {
  2885.             wc += WordCount( doc, node );
  2886.         }
  2887.     }
  2888.     return wc;
  2889. }
  2890.  
  2891.  
  2892. /**************************************************
  2893. * CheckFlicker
  2894. *
  2895. * Verify that the page does not cause flicker.
  2896. **************************************************/
  2897.  
  2898. static void CheckFlicker( TidyDocImpl* doc, Node* node )
  2899. {
  2900.     if ((doc->access.PRIORITYCHK == 1)||
  2901.         (doc->access.PRIORITYCHK == 2)||
  2902.         (doc->access.PRIORITYCHK == 3))
  2903.     {
  2904.         int msgcode = 0;
  2905.         if ( nodeIsSCRIPT(node) )
  2906.             msgcode = REMOVE_FLICKER_SCRIPT;
  2907.         else if ( nodeIsOBJECT(node) )
  2908.             msgcode = REMOVE_FLICKER_OBJECT;
  2909.         else if ( nodeIsEMBED(node) )
  2910.             msgcode = REMOVE_FLICKER_EMBED;
  2911.         else if ( nodeIsAPPLET(node) )
  2912.             msgcode = REMOVE_FLICKER_APPLET;
  2913.  
  2914.         /* Checks for animated gif within the <img> tag. */
  2915.         else if ( nodeIsIMG(node) )
  2916.         {
  2917.             AttVal* av = attrGetSRC( node );
  2918.             if ( hasValue(av) )
  2919.             {
  2920.                 tmbchar ext[20];
  2921.                 GetFileExtension( av->value, ext, sizeof(ext) );
  2922.                 if ( tmbstrcasecmp(ext, ".gif") == 0 )
  2923.                     msgcode = REMOVE_FLICKER_ANIMATED_GIF;
  2924.             }
  2925.         }            
  2926.  
  2927.         if ( msgcode )
  2928.             ReportAccessWarning( doc, node, msgcode );
  2929.     }
  2930. }
  2931.  
  2932.  
  2933. /**********************************************************
  2934. * CheckDeprecated
  2935. *
  2936. * APPLET, BASEFONT, CENTER, FONT, ISINDEX, 
  2937. * S, STRIKE, and U should not be used.  Becomes deprecated
  2938. * HTML if any of the above are used.
  2939. **********************************************************/
  2940.  
  2941. static void CheckDeprecated( TidyDocImpl* doc, Node* node )
  2942. {
  2943.     if ( doc->access.PRIORITYCHK == 2 ||
  2944.          doc->access.PRIORITYCHK == 3 )
  2945.     {
  2946.         int msgcode = 0;
  2947.         if ( nodeIsAPPLET(node) )
  2948.             msgcode = REPLACE_DEPRECATED_HTML_APPLET;
  2949.         else if ( nodeIsBASEFONT(node) )
  2950.             msgcode = REPLACE_DEPRECATED_HTML_BASEFONT;
  2951.         else if ( nodeIsCENTER(node) )
  2952.             msgcode = REPLACE_DEPRECATED_HTML_CENTER;
  2953.         else if ( nodeIsDIR(node) )
  2954.             msgcode = REPLACE_DEPRECATED_HTML_DIR;
  2955.         else if ( nodeIsFONT(node) )
  2956.             msgcode = REPLACE_DEPRECATED_HTML_FONT;
  2957.         else if ( nodeIsISINDEX(node) )
  2958.             msgcode = REPLACE_DEPRECATED_HTML_ISINDEX;
  2959.         else if ( nodeIsMENU(node) )
  2960.             msgcode = REPLACE_DEPRECATED_HTML_MENU;
  2961.         else if ( nodeIsS(node) )
  2962.             msgcode = REPLACE_DEPRECATED_HTML_S;
  2963.         else if ( nodeIsSTRIKE(node) )
  2964.             msgcode = REPLACE_DEPRECATED_HTML_STRIKE;
  2965.         else if ( nodeIsU(node) )
  2966.             msgcode = REPLACE_DEPRECATED_HTML_U;
  2967.  
  2968.         if ( msgcode )
  2969.             ReportAccessError( doc, node, msgcode );
  2970.     }
  2971. }
  2972.  
  2973.  
  2974. /************************************************************
  2975. * CheckScriptKeyboardAccessible
  2976. *
  2977. * Elements must have a device independent event handler if 
  2978. * they have any of the following device dependent event 
  2979. * handlers. 
  2980. ************************************************************/
  2981.  
  2982. static void CheckScriptKeyboardAccessible( TidyDocImpl* doc, Node* node )
  2983. {
  2984.     int HasOnMouseDown = 0;
  2985.     int HasOnMouseUp = 0;
  2986.     int HasOnClick = 0;
  2987.     int HasOnMouseOut = 0;
  2988.     int HasOnMouseOver = 0;
  2989.     int HasOnMouseMove = 0;
  2990.  
  2991.     if ( doc->access.PRIORITYCHK == 2 ||
  2992.          doc->access.PRIORITYCHK == 3 )
  2993.     {
  2994.         AttVal* av;
  2995.         /* Checks all elements for their attributes */
  2996.         for (av = node->attributes; av != NULL; av = av->next)
  2997.         {
  2998.             /* Must also have 'ONKEYDOWN' attribute with 'ONMOUSEDOWN' */
  2999.             if ( attrIsOnMOUSEDOWN(av) )
  3000.                 HasOnMouseDown++;
  3001.  
  3002.             /* Must also have 'ONKEYUP' attribute with 'ONMOUSEUP' */
  3003.             if ( attrIsOnMOUSEUP(av) )
  3004.                 HasOnMouseUp++;
  3005.  
  3006.             /* Must also have 'ONKEYPRESS' attribute with 'ONCLICK' */
  3007.             if ( attrIsOnCLICK(av) )
  3008.                 HasOnClick++;
  3009.  
  3010.             /* Must also have 'ONBLUR' attribute with 'ONMOUSEOUT' */
  3011.             if ( attrIsOnMOUSEOUT(av) )
  3012.                 HasOnMouseOut++;
  3013.  
  3014.             if ( attrIsOnMOUSEOVER(av) )
  3015.                 HasOnMouseOver++;
  3016.  
  3017.             if ( attrIsOnMOUSEMOVE(av) )
  3018.                 HasOnMouseMove++;
  3019.  
  3020.             if ( attrIsOnKEYDOWN(av) )
  3021.                 HasOnMouseDown++;
  3022.  
  3023.             if ( attrIsOnKEYUP(av) )
  3024.                 HasOnMouseUp++;
  3025.  
  3026.             if ( attrIsOnKEYPRESS(av) )
  3027.                 HasOnClick++;
  3028.  
  3029.             if ( attrIsOnBLUR(av) )
  3030.                 HasOnMouseOut++;
  3031.         }
  3032.  
  3033.         if ( HasOnMouseDown == 1 )
  3034.         {
  3035.             ReportAccessError( doc, node, SCRIPT_NOT_KEYBOARD_ACCESSIBLE_ON_MOUSE_DOWN);
  3036.         }
  3037.  
  3038.         if ( HasOnMouseUp == 1 )
  3039.         {
  3040.             ReportAccessError( doc, node, SCRIPT_NOT_KEYBOARD_ACCESSIBLE_ON_MOUSE_UP);
  3041.         }
  3042.         
  3043.         if ( HasOnClick == 1 )
  3044.         {
  3045.             ReportAccessError( doc, node, SCRIPT_NOT_KEYBOARD_ACCESSIBLE_ON_CLICK);
  3046.         }
  3047.             
  3048.         if ( HasOnMouseOut == 1 )
  3049.         {
  3050.             ReportAccessError( doc, node, SCRIPT_NOT_KEYBOARD_ACCESSIBLE_ON_MOUSE_OUT);
  3051.         }
  3052.         
  3053.         if ( HasOnMouseOver == 1 )
  3054.         {
  3055.             ReportAccessError( doc, node, SCRIPT_NOT_KEYBOARD_ACCESSIBLE_ON_MOUSE_OVER);
  3056.         }
  3057.  
  3058.         if ( HasOnMouseMove == 1 )
  3059.         {
  3060.             ReportAccessError( doc, node, SCRIPT_NOT_KEYBOARD_ACCESSIBLE_ON_MOUSE_MOVE);
  3061.         }
  3062.     }
  3063. }
  3064.  
  3065.  
  3066. /**********************************************************
  3067. * CheckMetaData
  3068. *
  3069. * Must have at least one of these elements in the document.
  3070. * META, LINK, TITLE or ADDRESS.  <meta> must contain 
  3071. * a "content" attribute that doesn't contain a URL, and
  3072. * an "http-Equiv" attribute that doesn't contain 'refresh'.
  3073. **********************************************************/
  3074.  
  3075.  
  3076. static Bool CheckMetaData( TidyDocImpl* doc, Node* node )
  3077. {
  3078.     Bool HasHttpEquiv = no;
  3079.     Bool HasContent = no;
  3080.     Bool HasRel = no;
  3081.     Bool ContainsAttr = no;
  3082.     Bool HasMetaData = no;
  3083.  
  3084.     if ( doc->access.PRIORITYCHK == 2 ||
  3085.          doc->access.PRIORITYCHK == 3 )
  3086.     {
  3087.         if ( nodeIsMETA(node) )
  3088.         {
  3089.             AttVal* av;
  3090.             for (av = node->attributes; av != NULL; av = av->next)
  3091.             {
  3092.                 if ( attrIsHTTP_EQUIV(av) && hasValue(av) )
  3093.                 {
  3094.                     ContainsAttr = yes;
  3095.  
  3096.                     /* Must not have an auto-refresh */
  3097.                     if (AttrValueIs(av, "refresh"))
  3098.                     {
  3099.                         HasHttpEquiv = yes;
  3100.                         ReportAccessError( doc, node, REMOVE_AUTO_REFRESH );
  3101.                     }
  3102.                 }
  3103.  
  3104.                 if ( attrIsCONTENT(av) && hasValue(av) )
  3105.                 {
  3106.                     ContainsAttr = yes;
  3107.  
  3108.                     /* If the value is not an integer, then it must not be a URL */
  3109.                     if ( tmbstrncmp(av->value, "http:", 5) == 0)
  3110.                     {
  3111.                         HasContent = yes;
  3112.                         ReportAccessError( doc, node, REMOVE_AUTO_REDIRECT);
  3113.                     }
  3114.                 }
  3115.             }
  3116.         
  3117.             if ( HasContent || HasHttpEquiv )
  3118.             {
  3119.                 HasMetaData = yes;
  3120.                 ReportAccessError( doc, node, METADATA_MISSING_REDIRECT_AUTOREFRESH);
  3121.             }
  3122.             else
  3123.             {
  3124.                 if ( ContainsAttr && !HasContent && !HasHttpEquiv )
  3125.                     HasMetaData = yes;                    
  3126.             }
  3127.         }
  3128.  
  3129.         if ( !HasMetaData && 
  3130.              nodeIsADDRESS(node) &&
  3131.              nodeIsA(node->content) )
  3132.         {
  3133.             HasMetaData = yes;
  3134.         }
  3135.             
  3136.         if ( !HasMetaData &&
  3137.              nodeIsTITLE(node) &&
  3138.              nodeIsText(node->content) )
  3139.         {
  3140.             tmbstr word = textFromOneNode( doc, node->content );
  3141.             if ( !IsWhitespace(word) )
  3142.                 HasMetaData = yes;
  3143.         }
  3144.  
  3145.         if ( !HasMetaData &&
  3146.              nodeIsLINK(node) )
  3147.         {
  3148.             AttVal* av = attrGetREL(node);
  3149.             HasMetaData = yes;
  3150.  
  3151.             if (AttrContains(av, "stylesheet"))
  3152.             {
  3153.                 HasRel = yes;
  3154.                 ReportAccessError( doc, node, METADATA_MISSING_LINK );
  3155.             }
  3156.         }
  3157.             
  3158.         /* Check for MetaData */
  3159.         for ( node = node->content; !HasMetaData && node; node = node->next )
  3160.         {
  3161.             HasMetaData = CheckMetaData( doc, node);
  3162.         }
  3163.     }
  3164.     return HasMetaData;
  3165. }
  3166.  
  3167.  
  3168. /*******************************************************
  3169. * MetaDataPresent
  3170. *
  3171. * Determines if MetaData is present in document
  3172. *******************************************************/
  3173.  
  3174. static void MetaDataPresent( TidyDocImpl* doc, Node* node )
  3175. {
  3176.     if ( doc->access.PRIORITYCHK == 2 ||
  3177.          doc->access.PRIORITYCHK == 3 )
  3178.     {
  3179.         ReportAccessError( doc, node, METADATA_MISSING );
  3180.     }
  3181. }
  3182.  
  3183.  
  3184. /*****************************************************
  3185. * CheckDocType
  3186. *
  3187. * Checks that every HTML/XHTML document contains a 
  3188. * '!DOCTYPE' before the root node. ie.  <HTML>
  3189. *****************************************************/
  3190.  
  3191. static void CheckDocType( TidyDocImpl* doc, Node* node )
  3192. {
  3193.     tmbstr word;
  3194.  
  3195.     if ((doc->access.PRIORITYCHK == 2)||
  3196.         (doc->access.PRIORITYCHK == 3))
  3197.     {
  3198.         if (node->tag == NULL)
  3199.         {
  3200.             word = textFromOneNode( doc, node->content);
  3201.                 
  3202.             if ((strstr (word, "HTML PUBLIC") == NULL) &&
  3203.                 (strstr (word, "html PUBLIC") == NULL))
  3204.             {
  3205.                 ReportAccessError( doc, node, DOCTYPE_MISSING);
  3206.             }
  3207.         }
  3208.     }
  3209. }
  3210.  
  3211.  
  3212.  
  3213. /********************************************************
  3214. * CheckMapLinks
  3215. *
  3216. * Checks to see if an HREF for A element matches HREF
  3217. * for AREA element.  There must be an HREF attribute 
  3218. * of an A element for every HREF of an AREA element. 
  3219. ********************************************************/
  3220.  
  3221. static Bool urlMatch( ctmbstr url1, ctmbstr url2 )
  3222. {
  3223.   /* TODO: Make host part case-insensitive and
  3224.   ** remainder case-sensitive.
  3225.   */
  3226.   return ( tmbstrcmp( url1, url2 ) == 0 );
  3227. }
  3228.  
  3229. static Bool FindLinkA( TidyDocImpl* doc, Node* node, ctmbstr url )
  3230. {
  3231.   Bool found = no;
  3232.   for ( node = node->content; !found && node; node = node->next )
  3233.   {
  3234.     if ( nodeIsA(node) )
  3235.     {
  3236.       AttVal* href = attrGetHREF( node );
  3237.       found = ( hasValue(href) && urlMatch(url, href->value) );
  3238.     }
  3239.     else
  3240.         found = FindLinkA( doc, node, url );
  3241.   }
  3242.   return found;
  3243. }
  3244.  
  3245. static void CheckMapLinks( TidyDocImpl* doc, Node* node )
  3246. {
  3247.     Node* child = node->content;
  3248.  
  3249.     /* Stores the 'HREF' link of an AREA element within a MAP element */
  3250.     for ( child = node->content; child != NULL; child = child->next )
  3251.     {
  3252.         if ( nodeIsAREA(child) )
  3253.         {
  3254.             /* Checks for 'HREF' attribute */                
  3255.             AttVal* href = attrGetHREF( child );
  3256.             if ( hasValue(href) &&
  3257.                  !FindLinkA( doc, &doc->root, href->value ) )
  3258.             {
  3259.                 ReportAccessError( doc, node, IMG_MAP_CLIENT_MISSING_TEXT_LINKS );
  3260.             }
  3261.         }
  3262.     }
  3263. }
  3264.  
  3265.  
  3266. /****************************************************
  3267. * CheckForStyleAttribute
  3268. *
  3269. * Checks all elements within the document to check 
  3270. * for the use of 'STYLE' attribute.
  3271. ****************************************************/
  3272.  
  3273. static void CheckForStyleAttribute( TidyDocImpl* doc, Node* node )
  3274. {
  3275.     if ( doc->access.PRIORITYCHK == 1 ||
  3276.          doc->access.PRIORITYCHK == 2 ||
  3277.          doc->access.PRIORITYCHK == 3 )
  3278.     {
  3279.         /* Must not contain 'STYLE' attribute */
  3280.         AttVal* style = attrGetSTYLE( node );
  3281.         if ( hasValue(style) )
  3282.         {
  3283.             ReportAccessWarning( doc, node, STYLESHEETS_REQUIRE_TESTING_STYLE_ATTR );
  3284.         }
  3285.     }
  3286. }
  3287.  
  3288.  
  3289. /*****************************************************
  3290. * CheckForListElements
  3291. *
  3292. * Checks document for list elements (<ol>, <ul>, <li>)
  3293. *****************************************************/
  3294.  
  3295. static void CheckForListElements( TidyDocImpl* doc, Node* node )
  3296. {
  3297.     if ( nodeIsLI(node) )
  3298.     {
  3299.         doc->access.ListElements++;
  3300.     }
  3301.     else if ( nodeIsOL(node) || nodeIsUL(node) )
  3302.     {
  3303.         doc->access.OtherListElements++;
  3304.     }
  3305.  
  3306.     for ( node = node->content; node != NULL; node = node->next )
  3307.     {
  3308.         CheckForListElements( doc, node );
  3309.     }
  3310. }
  3311.  
  3312.  
  3313. /******************************************************
  3314. * CheckListUsage
  3315. *
  3316. * Ensures that lists are properly used.  <ol> and <ul>
  3317. * must contain <li> within itself, and <li> must not be
  3318. * by itself.
  3319. ******************************************************/
  3320.  
  3321. static void CheckListUsage( TidyDocImpl* doc, Node* node )
  3322. {
  3323.     int msgcode = 0;
  3324.     if ( nodeIsOL(node) )
  3325.         msgcode = LIST_USAGE_INVALID_OL;
  3326.     else if ( nodeIsUL(node) )
  3327.         msgcode = LIST_USAGE_INVALID_UL;
  3328.  
  3329.     if ( msgcode )
  3330.     {
  3331.         if ( !nodeIsLI(node->content) )
  3332.             ReportAccessWarning( doc, node, msgcode );
  3333.     }
  3334.     else if ( nodeIsLI(node) )
  3335.     {
  3336.         /* Check that LI parent 
  3337.         ** a) exists,
  3338.         ** b) is either OL or UL
  3339.         */
  3340.         if ( node->parent == NULL ||
  3341.              ( !nodeIsOL(node->parent) && !nodeIsUL(node->parent) ) )
  3342.         {
  3343.             ReportAccessWarning( doc, node, LIST_USAGE_INVALID_LI );
  3344.         }
  3345.     }
  3346. }
  3347.  
  3348. /************************************************************
  3349. * InitAccessibilityChecks
  3350. *
  3351. * Initializes the AccessibilityChecks variables as necessary
  3352. ************************************************************/
  3353.  
  3354. void InitAccessibilityChecks( TidyDocImpl* doc, int level123 )
  3355. {
  3356.     ClearMemory( &doc->access, sizeof(doc->access) );
  3357.     doc->access.PRIORITYCHK = level123;
  3358. }
  3359.  
  3360. /************************************************************
  3361. * CleanupAccessibilityChecks
  3362. *
  3363. * Cleans up the AccessibilityChecks variables as necessary
  3364. ************************************************************/
  3365.  
  3366.  
  3367. void FreeAccessibilityChecks( TidyDocImpl* doc )
  3368. {
  3369. #pragma unused(doc)
  3370.  
  3371.     /* free any memory allocated for the lists
  3372.  
  3373.     Linked List of Links not used.  Just search document as 
  3374.     AREA tags are encountered.  Same algorithm, but no
  3375.     data structures necessary.
  3376.  
  3377.     current = start;
  3378.     while (current)
  3379.     {
  3380.         void    *templink = (void *)current;
  3381.         
  3382.         current = current->next;
  3383.         MemFree(templink);
  3384.     }
  3385.     start = NULL;
  3386.     */
  3387. }
  3388.  
  3389. /************************************************************
  3390. * AccessibilityChecks
  3391. *
  3392. * Traverses through the individual nodes of the tree
  3393. * and checks attributes and elements for accessibility.
  3394. * after the tree structure has been formed.
  3395. ************************************************************/
  3396.  
  3397. static void AccessibilityCheckNode( TidyDocImpl* doc, Node* node )
  3398. {
  3399.     Node* content;
  3400.     
  3401.     /* Check BODY for color contrast */
  3402.     if ( nodeIsBODY(node) )
  3403.     {
  3404.         CheckColorContrast( doc, node );
  3405.     }
  3406.  
  3407.     /* Checks document for MetaData */
  3408.     else if ( nodeIsHEAD(node) )
  3409.     {
  3410.         if ( !CheckMetaData( doc, node ) )
  3411.           MetaDataPresent( doc, node );
  3412.     }
  3413.     
  3414.     /* Check the ANCHOR tag */
  3415.     else if ( nodeIsA(node) )
  3416.     {
  3417.         CheckAnchorAccess( doc, node );
  3418.     }
  3419.  
  3420.     /* Check the IMAGE tag */
  3421.     else if ( nodeIsIMG(node) )
  3422.     {
  3423.         CheckFlicker( doc, node );
  3424.         CheckColorAvailable( doc, node );
  3425.         CheckImage( doc, node );
  3426.     }
  3427.  
  3428.         /* Checks MAP for client-side text links */
  3429.     else if ( nodeIsMAP(node) )
  3430.     {
  3431.         CheckMapLinks( doc, node );
  3432.     }
  3433.  
  3434.     /* Check the AREA tag */
  3435.     else if ( nodeIsAREA(node) )
  3436.     {
  3437.         CheckArea( doc, node );
  3438.     }
  3439.  
  3440.     /* Check the APPLET tag */
  3441.     else if ( nodeIsAPPLET(node) )
  3442.     {
  3443.         CheckDeprecated( doc, node );
  3444.         ProgrammaticObjects( doc, node );
  3445.         DynamicContent( doc, node );
  3446.         AccessibleCompatible( doc, node );
  3447.         CheckFlicker( doc, node );
  3448.         CheckColorAvailable( doc, node );
  3449.         CheckApplet(doc, node );
  3450.     }
  3451.     
  3452.     /* Check the OBJECT tag */
  3453.     else if ( nodeIsOBJECT(node) )
  3454.     {
  3455.         ProgrammaticObjects( doc, node );
  3456.         DynamicContent( doc, node );
  3457.         AccessibleCompatible( doc, node );
  3458.         CheckFlicker( doc, node );
  3459.         CheckColorAvailable( doc, node );
  3460.         CheckObject( doc, node );
  3461.     }
  3462.     
  3463.     /* Check the FRAME tag */
  3464.     else if ( nodeIsFRAME(node) )
  3465.     {
  3466.         CheckFrame( doc, node );
  3467.     }
  3468.     
  3469.     /* Check the IFRAME tag */
  3470.     else if ( nodeIsIFRAME(node) )
  3471.     {
  3472.         CheckIFrame( doc, node );
  3473.     }
  3474.     
  3475.     /* Check the SCRIPT tag */
  3476.     else if ( nodeIsSCRIPT(node) )
  3477.     {
  3478.         DynamicContent( doc, node );
  3479.         ProgrammaticObjects( doc, node );
  3480.         AccessibleCompatible( doc, node );
  3481.         CheckFlicker( doc, node );
  3482.         CheckColorAvailable( doc, node );
  3483.         CheckScriptAcc( doc, node );
  3484.     }
  3485.  
  3486.     /* Check the TABLE tag */
  3487.     else if ( nodeIsTABLE(node) )
  3488.     {
  3489.         CheckColorContrast( doc, node );
  3490.         CheckTable( doc, node );
  3491.     }
  3492.  
  3493.     /* Check the PRE for ASCII art */
  3494.     else if ( nodeIsPRE(node) || nodeIsXMP(node) )
  3495.     {
  3496.         CheckASCII( doc, node );
  3497.     }
  3498.  
  3499.     /* Check the LABEL tag */
  3500.     else if ( nodeIsLABEL(node) )
  3501.     {
  3502.         CheckLabel( doc, node );
  3503.     }
  3504.  
  3505.     /* Check INPUT tag for validity */
  3506.     else if ( nodeIsINPUT(node) )
  3507.     {
  3508.         CheckColorAvailable( doc, node );
  3509.         CheckInputLabel( doc, node );
  3510.         CheckInputAttributes( doc, node );
  3511.     }
  3512.  
  3513.     /* Checks FRAMESET element for NOFRAME section */
  3514.     else if ( nodeIsFRAMESET(node) )
  3515.     {
  3516.         CheckFrameSet( doc, node );
  3517.     }
  3518.     
  3519.     /* Checks for header elements for valid header increase */
  3520.     else if ( nodeIsHeader(node) )
  3521.     {
  3522.         CheckHeaderNesting( doc, node );
  3523.     }
  3524.  
  3525.     /* Checks P element to ensure that it is not a header */
  3526.     else if ( nodeIsP(node) )
  3527.     {
  3528.         CheckParagraphHeader( doc, node );
  3529.     }
  3530.  
  3531.     /* Checks SELECT element for LABEL */
  3532.     else if ( nodeIsSELECT(node) )
  3533.     {
  3534.         CheckSelect( doc, node );
  3535.     }
  3536.  
  3537.     /* Checks TEXTAREA element for LABEL */
  3538.     else if ( nodeIsTEXTAREA(node) )
  3539.     {
  3540.         CheckTextArea( doc, node );
  3541.     }
  3542.  
  3543.     /* Checks HTML elemnt for valid 'LANG' */
  3544.     else if ( nodeIsHTML(node) )
  3545.     {
  3546.         CheckHTMLAccess( doc, node );
  3547.     }
  3548.  
  3549.     /* Checks BLINK for any blinking text */
  3550.     else if ( nodeIsBLINK(node) )
  3551.     {
  3552.         CheckBlink( doc, node );
  3553.     }
  3554.  
  3555.     /* Checks MARQUEE for any MARQUEE text */
  3556.     else if ( nodeIsMARQUEE(node) )
  3557.     {
  3558.         CheckMarquee( doc, node );
  3559.     }
  3560.  
  3561.     /* Checks LINK for 'REL' attribute */
  3562.     else if ( nodeIsLINK(node) )
  3563.     {
  3564.         CheckLink( doc, node );
  3565.     }
  3566.  
  3567.     /* Checks to see if STYLE is used */
  3568.     else if ( nodeIsSTYLE(node) )
  3569.     {
  3570.         CheckColorContrast( doc, node );
  3571.         CheckStyle( doc, node );
  3572.     }
  3573.  
  3574.     /* Checks to see if EMBED is used */
  3575.     else if ( nodeIsEMBED(node) )
  3576.     {
  3577.         CheckEmbed( doc, node );
  3578.         ProgrammaticObjects( doc, node );
  3579.         AccessibleCompatible( doc, node );
  3580.         CheckFlicker( doc, node );
  3581.     }
  3582.  
  3583.     /* Deprecated HTML if the following tags are found in the document */
  3584.     else if ( nodeIsBASEFONT(node) ||
  3585.               nodeIsCENTER(node)   ||
  3586.               nodeIsISINDEX(node)  ||
  3587.               nodeIsU(node)        ||
  3588.               nodeIsFONT(node)     ||
  3589.               nodeIsDIR(node)      ||
  3590.               nodeIsS(node)        ||
  3591.               nodeIsSTRIKE(node)   ||
  3592.               nodeIsMENU(node) )
  3593.     {
  3594.         CheckDeprecated( doc, node );
  3595.     }
  3596.  
  3597.     /* Checks for 'ABBR' attribute if needed */
  3598.     else if ( nodeIsTH(node) )
  3599.     {
  3600.         CheckTH( doc, node );
  3601.     }
  3602.  
  3603.     /* Ensures that lists are properly used */
  3604.     else if ( nodeIsLI(node) || nodeIsOL(node) || nodeIsUL(node) )
  3605.     {
  3606.         CheckListUsage( doc, node );
  3607.     }
  3608.  
  3609.     /* Recursively check all child nodes.
  3610.     */
  3611.     for ( content = node->content; content != NULL; content = content->next )
  3612.     {
  3613.         AccessibilityCheckNode( doc, content );
  3614.     }
  3615. }
  3616.  
  3617.  
  3618. void AccessibilityChecks( TidyDocImpl* doc )
  3619. {
  3620.     /* Initialize */
  3621.     InitAccessibilityChecks( doc, cfg(doc, TidyAccessibilityCheckLevel) );
  3622.  
  3623.     /* Hello there, ladies and gentlemen... */
  3624.     AccessibilityHelloMessage( doc );
  3625.  
  3626.     /* Checks all elements for script accessibility */
  3627.     CheckScriptKeyboardAccessible( doc, &doc->root );
  3628.  
  3629.     /* Checks entire document for the use of 'STYLE' attribute */
  3630.     CheckForStyleAttribute( doc, &doc->root );
  3631.  
  3632.     /* Checks for '!DOCTYPE' */
  3633.     CheckDocType( doc, &doc->root );
  3634.  
  3635.     
  3636.     /* Checks to see if stylesheets are used to control the layout */
  3637.     if ( ! CheckMissingStyleSheets( doc, &doc->root ) )
  3638.     {
  3639.         ReportAccessWarning( doc, &doc->root, STYLE_SHEET_CONTROL_PRESENTATION );
  3640.     }
  3641.  
  3642.     /* Check to see if any list elements are found within the document */
  3643.     CheckForListElements( doc, &doc->root );
  3644.  
  3645.     /* Checks for natural language change */
  3646.     /* Must contain more than 3 words of text in the document
  3647.     **
  3648.     ** CPR - Not sure what intent is here, but this 
  3649.     ** routine has nothing to do with changes in language.
  3650.     ** It seems like a bad idea to emit this message for
  3651.     ** every document with _more_ than 3 words!
  3652.  
  3653.     if ( WordCount(doc, &doc->root) > 3 )
  3654.     {
  3655.         ReportAccessWarning( doc, node, INDICATE_CHANGES_IN_LANGUAGE);
  3656.     }
  3657.     */
  3658.  
  3659.  
  3660.     /* Recursively apply all remaining checks to 
  3661.     ** each node in document.
  3662.     */
  3663.     AccessibilityCheckNode( doc, &doc->root );
  3664.  
  3665.     /* Cleanup */
  3666.     FreeAccessibilityChecks( doc );
  3667. }
  3668.  
  3669. #endif
  3670.